2017-10-16 77 views
2
package main 

import (
    "fmt" 
    "encoding/json" 
) 

type Ticket struct { 
    From string 
    To string 
} 

func (t Ticket) String() string { 
    return fmt.Sprintf("%s - %s", t.From, t.To) 
} 

type Passenger struct { 
    Name string `json:"Name"` 
    Tkt Ticket `json:"Ticket"` 
} 

func main() { 
    p := Passenger{} 
    p.Name = "John" 
    p.Tkt.From = "New York" 
    p.Tkt.To = "Washington" 

    buf, _ := json.Marshal(p) 
    fmt.Println(string(buf)) 
} 

此代码输出:如何正确使用String()方法将嵌入式结构序列化为JSON字符串?

{"Name":"John","Ticket":{"From":"New York","To":"Washington"}} 

但是,使用json.Marshal()方法(方法很简单,友好的复杂结构),如何使它的输出是这样的:

{"Name":"John","Ticket":"New York - Washington"} 

回答

3

要生成的JSON Go值的表示,encoding/json包检查值是否实现了json.Marshalerencoding.TextMarshaler接口,如果是,则使用/调用它们(按此顺序)。这记录在json.Marshal()

元帅递归地遍历值v。如果遇到的值实现了Marshaler接口并且不是零指针,则Marshal会调用其MarshalJSON方法生成JSON。如果没有MarshalJSON方法存在,但该值实现的是encoding.TextMarshaler,Marshal会调用它的MarshalText方法并将结果编码为JSON字符串。

json/encoding包不关心String()方法。所以,如果你想控制你的值(Ticket结构)的JSON表示/输出,实现它​​json.Marshaler(其中你可以叫String()到您喜欢):

func (t Ticket) MarshalJSON() ([]byte, error) { 
    return []byte(`"` + t.String() + `"`), nil 
} 

那么输出会随着你欲望:

{"Name":"John","Ticket":"New York - Washington"} 

Go Playground上试试。

有一点要注意的事项:如果Ticket.String()产生的string将包含引号",输出会变得无效JSON,或者更可能json.Marshal()将返回一个错误。

采取这样逃逸的护理,最好/最简单的就是使用json包本身:它告诉JSON编码的Ticket.String()string结果:

func (t Ticket) MarshalJSON() ([]byte, error) { 
    return json.Marshal(t.String()) 
} 

现在,如果我们测试它像这样:

p.Name = "John" 
p.Tkt.From = "New\" York" // CONTAINS QUOTE 
p.Tkt.To = "Washington" 

buf, err := json.Marshal(p) 
fmt.Println(string(buf), err) 

输出将仍然是一个有效的JSON(尝试在Go Playground):

{"Name":"John","Ticket":"New\" York - Washington"} <nil> 
+0

精彩的解决方案。谢谢。 –

+0

顺便提一句,你可以使用'%q'而不是'%s',它会为你引用字符串。 https://play.golang.org/p/RYhaMM5kpd – Kaedys

+1

@Kaedys你让我意识到我忘记了输入中的潜在引用。我更新了这个问题,建议使用'json'包来处理这些问题。 – icza

相关问题