Given the following structs:
type Person struct {
Name string `json:"name"`
}
type Employee struct {
*Person
JobRole string `json:"jobRole"`
}
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))
Output:
{"name":"Bob","jobRole":"Sales"}
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),
})
}
it breaks entirely:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Now results in:
{"name":"BOB"}
(Note the conspicuous lack of jobRole
field)
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:
{"name":"BOB","jobRole":"Sales"}
That is, encode Employee
's fields normally, and defer to Person
's MarshalJSON()
method to marshal its fields, and hand back some tidy JSON.
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?
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"`
}
Example: https://play.golang.org/p/u96T4C6PaY
Update
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
https://play.golang.org/p/ut3e21oRdj
Nearly 4 years later, I've come up with an answer that is fundamentally similar to @jcbwlkr's, but does not require the intermediate unmarshal/re-marshal step, by using a little bit of byte-slice manipulation to join two JSON segments.
func (e *Employee) MarshalJSON() ([]byte, error) {
pJSON, err := e.Person.MarshalJSON()
if err != nil {
return nil, err
}
eJSON, err := json.Marshal(map[string]interface{}{
"jobRole": e.JobRole,
})
if err != nil {
return nil, err
}
eJSON[0] = ','
return append(pJSON[:len(pJSON)-1], eJSON...), nil
}
Additional details and discussion of this approach here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With