问题描述
给出以下结构:
type Person {
Name string `json:"name"`
}
type Employee {
Person
JobRole string `json:"jobRole"`
}
我可以轻松地按预期将雇员编组为JSON:
I can easily marshal an Employee to JSON as expected:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
输出:
但是当嵌入式结构具有自定义的 MarshalJSON()
方法...
But when the embedded struct has a custom MarshalJSON()
method...
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
字段)
(Note the conspicuous lack of jobRole
field)
这很容易预料到……嵌入的 Person
struct实现了 MarshalJSON()
函数,该函数被调用。
This is easily anticipated... the embedded Person
struct implements the MarshalJSON()
function, which is being called.
麻烦的是,这不是什么我想要。我想要的是:
The trouble is, it's not what I want. What I want would be:
也就是说,通常对 Employee
的字段进行编码,并推迟到 Person
的 MarshalJSON()
方法来整理其字段,并交出一些整洁的JSON。
That is, encode Employee
's fields normally, and defer to Person
's MarshalJSON()
method to marshal its fields, and hand back some tidy JSON.
现在我也可以向 Employee
添加 MarshalJSON()
方法,但这需要我知道嵌入类型也实现了 MarshalJSON()
,并且(a)复制其逻辑,或(b)调用 Person
的 MarshalJSON()
并以某种方式操纵其输出以适合我的需要。哪一种方法似乎都是草率的,而且还不是很有前途的(如果我某天无法控制的嵌入式类型会添加自定义的 MarshalJSON()
方法怎么办?)
Now I could add a MarshalJSON()
method to Employee
as well, but this requires that I know that the embedded type implements MarshalJSON()
as well, and either (a) duplicate its logic, or (b) call Person
's MarshalJSON()
and somehow manipulate its output to fit where I want it. Either approach seems sloppy, and not very future proof (what if an embedded type I don't control some day adds a custom MarshalJSON()
method?)
这里有我没有考虑过的替代方案吗?
Are there any alternatives here that I haven't considered?
推荐答案
不要将 MarshalJSON
放在 Person
上,因为这已被提升为外部类型。而是使用类型名称字符串
并使用 Name
实现 MarshalJSON
。然后将 Person
更改为
Don't put MarshalJSON
on Person
since that's being promoted to the outer type. Instead make a type Name string
and have Name
implement MarshalJSON
. Then change Person
to
type Person struct {
Name Name `json:"name"`
}
示例:
更新
要更一般地解决此问题,您将必须实现 MarshalJSON
放在外部类型上。内部类型的方法被提升为外部类型,因此您不会去解决这个问题。您可以让外部类型调用内部类型的 MarshalJSON
,然后将其解组为通用结构,例如 map [string] interface {}
并添加您自己的字段。这个示例可以做到,但是它具有更改最终输出字段顺序的副作用
To solve this more generically you're going to have to implement MarshalJSON
on the outer type. Methods on the inner type are promoted to the outer type so you're not going to get around that. You could have the outer type call the inner type's MarshalJSON
then unmarshal that into a generic structure like map[string]interface{}
and add your own fields. This example does that but it has a side effect of changing the order of the final output fields
这篇关于使用自定义MarshalJSON()方法嵌入结构的惯用方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!