用自定义MarshalJSON()方法嵌入struct的惯用法

Fli*_*mzy 5 json embedding go

鉴于以下结构:

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

type Employee {
    Person
    JobRole string `json:"jobRole"`
}
Run Code Online (Sandbox Code Playgroud)

我可以按照预期轻松地将Employee编组为JSON:

p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Run Code Online (Sandbox Code Playgroud)

输出:

{ "名": "鲍勃", "jobRole": "销售"}

但是当嵌入式结构有一个自定义MarshalJSON()方法时......

func (p *Person) MarshalJSON() ([]byte,error) {
    return json.Marshal(struct{
        Name string `json:"name"`
    }{
        Name: strings.ToUpper(p.Name),
    })
}
Run Code Online (Sandbox Code Playgroud)

它彻底打破了:

p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Run Code Online (Sandbox Code Playgroud)

现在结果是:

{ "名": "BOB"}

(注意显着缺乏jobRole领域)

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

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

{ "名": "BOB", "jobRole": "销售"}

也就是说,Employee正常编码的字段,并推迟编组其字段PersonMarshalJSON()方法,并交回一些整洁的JSON.

现在,我可以一个添加MarshalJSON()方法Employee为好,但这个要求,我知道,嵌入式类型实现MarshalJSON(),以及,或者(a)复制它的逻辑,或(b)调用PersonMarshalJSON()并以某种方式操纵它的输出,以适应,我想它.这两种方法看起来都很邋and,而且不是很有前途的证明(如果某天我不控制的嵌入式类型会增加一个自定义MarshalJSON()方法怎么办?)

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

jcb*_*lkr 7

不要穿MarshalJSON,Person因为它被提升为外部类型.而是制定type Name stringName实施MarshalJSON.然后Person改为

type Person struct {
    Name Name `json:"name"`
}
Run Code Online (Sandbox Code Playgroud)

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


更新

为了更普遍地解决这个问题,你将不得不MarshalJSON在外部类型上实现.内部类型的方法被提升为外部类型,所以你不会绕过它.你可以让外部类型调用内部类型,MarshalJSON然后将其解组为通用结构,map[string]interface{}并添加自己的字段.此示例执行此操作但它具有更改最终输出字段的顺序的副作用

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