给出以下结构:

type Person {
    Name string `json:"name"`
}

type Employee {
    Person
    JobRole string `json:"jobRole"`
}

我可以按预期轻松地将Employee编码为JSON:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))

输出:



但是当嵌入式结构具有自定义MarshalJSON()方法时...
func (p *Person) MarshalJSON() ([]byte,error) {
    return json.Marshal(struct{
        Name string `json:"name"`
    }{
        Name: strings.ToUpper(p.Name),
    })
}

它完全破裂:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))

现在结果为:



(请注意,缺少jobRole字段)

这很容易预料...嵌入式Person结构实现了MarshalJSON()函数,该函数被调用。

麻烦是,这不是我想要的。我想要的是:



也就是说,通常对Employee的字段进行编码,并遵循PersonMarshalJSON()方法来编码(marshal)其字段,并交出一些整洁的JSON。

现在,我也可以在MarshalJSON()中添加Employee方法,但这要求我知道嵌入式类型也实现MarshalJSON(),并且(a)复制其逻辑,或者(b)调用PersonMarshalJSON()并以某种方式操纵其输出适合我想要的地方。这两种方法似乎都是草率的,并且不是很可靠的证明(如果我某天无法控制的嵌入式类型会添加自定义MarshalJSON()方法,该怎么办?)

这里有没有我没有考虑过的替代方案?

最佳答案

不要将MarshalJSON放在Person上,因为它已被提升为外部类型。而是制作一个type Name string,并让Name实现MarshalJSON。然后将Person更改为

type Person struct {
    Name Name `json:"name"`
}

示例:https://play.golang.org/p/u96T4C6PaY

更新

为了更一般地解决这个问题,您将必须在外部类型上实现MarshalJSON。内部类型上的方法被提升为外部类型,因此您不会去解决这个问题。您可以让外部类型调用内部类型的MarshalJSON,然后将其解码为类似于map[string]interface{}的通用结构,然后添加自己的字段。此示例这样做了,但它具有更改最终输出字段的顺序的副作用

https://play.golang.org/p/ut3e21oRdj

关于json - 使用自定义MarshalJSON()方法嵌入结构的惯用方式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38489776/

10-09 12:41