如何正确地将带有String()方法的嵌入式结构序列化为JSON字符串?

如何正确地将带有String()方法的嵌入式结构序列化为JSON字符串?,json,string,go,serialization,format,Json,String,Go,Serialization,Format,此代码输出: 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 `j

此代码输出:

 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))
}
但是,使用
json.Marshal()
method(对于复杂结构来说,这既简单又友好),如何使其输出如下:

 {"Name":"John","Ticket":{"From":"New York","To":"Washington"}}
func (t Ticket) MarshalJSON() ([]byte, error) {
    return json.Marshal(t.String())
}

为了生成Go值的JSON表示,包将检查该值是否实现了或接口,如果实现了,则使用/调用它们(按此顺序)。该文件记录在:

封送处理递归地遍历值v。如果遇到的值实现了Marshaler接口,并且不是nil指针,Marshall将调用其MarshalJSON方法来生成JSON。如果不存在MarshalJSON方法,但该值实现encoding.TextMarshaler,则Marshal将调用其MarshalText方法并将结果编码为JSON字符串

json/encoding
包不关心
String()
方法。因此,如果您想控制值的JSON表示/输出(
Ticket
struct),请在其上实现
JSON.Marshaler
(您可以根据自己的喜好调用
String()
):

然后输出将如您所愿:

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

需要注意一件事:如果
Ticket.string()
生成的
字符串包含引号
,则输出将成为无效的JSON,或者更可能的是
JSON.Marshal()
将返回错误

要处理这种转义,最好/最简单的方法是使用
json
包本身:告诉它对
Ticket.string()的
string
结果进行json编码:

{"Name":"John","Ticket":"New York - Washington"}
现在,如果我们这样测试它:

 {"Name":"John","Ticket":{"From":"New York","To":"Washington"}}
func (t Ticket) MarshalJSON() ([]byte, error) {
    return json.Marshal(t.String())
}
输出仍将是有效的JSON(请在上尝试):

{“姓名”:“约翰”,“票证”:“纽约\“纽约-华盛顿”}

绝妙的解决方案。谢谢。顺便说一下,您可以使用
%q
而不是
%s
,它会为您引用字符串@Kaedys你让我意识到我忘了输入中的潜在引号。我更新了这个问题,建议使用
json
包本身来处理这些问题。这个调用很好,不过
%q
标记到
fmt.Sprintf()
也会自动转义输入字符串中的任何引号。@Kaedys是的,在这种情况下这两个都可以工作。但一般来说,最好将转义留给将处理所有情况的
json
包。
{"Name":"John","Ticket":"New\" York - Washington"} <nil>