使用自定义MarshalJSON()方法嵌入结构的惯用方式

Given the following structs:

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

type Employee {
    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
", 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
", 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