Preserving Custom Marshalling for Embedded Structs
In Go, embedding one struct into another is a common way to inherit functionality. However, when the embedded struct has a custom MarshalJSON() method, issues can arise. This article explores a solution to this challenge, ensuring that the outer struct can marshal its fields normally while still leveraging the custom marshalling of the embedded struct.
Consider the following structs:
type Person struct { Name string `json:"name"` } type Employee struct { *Person JobRole string `json:"jobRole"` }
Normally, marshalling an Employee to JSON would produce the expected output:
p := Person{"Bob"} e := Employee{&p, "Sales"} output, _ := json.Marshal(e) fmt.Printf("%s\n", string(output)) // Output: {"name":"Bob","jobRole":"Sales"}
However, introducing a custom MarshalJSON() method for the embedded Person struct changes this behavior:
func (p *Person) MarshalJSON() ([]byte, error) { return json.Marshal(struct{ Name string `json:"name"` }{Name: strings.ToUpper(p.Name)}) }
Now, marshalling the Employee produces only the uppercased name:
output, _ := json.Marshal(e) fmt.Printf("%s\n", string(output)) // Output: {"name":"BOB"}
To resolve this, one might attempt to add a MarshalJSON() method to the outer Employee struct. However, this approach requires knowledge of the embedded type's custom marshalling, which may not always be practical.
A more generic solution involves directly implementing MarshalJSON() on the outer type:
// Note: not on Person func (e *Employee) MarshalJSON() ([]byte, error) { inner, err := json.MarshalIndent((*e.Person).(*Person), "", " ") if err != nil { return nil, err } b := []byte(strings.Replace(string(inner), "}", "}", -1)) b = append(b, []byte(`,"jobRole":"`+e.JobRole+`"}`)...) return b, nil }
This approach calls the embedded struct's MarshalJSON() method, converts the result to a map, and adds the outer struct's fields to produce the desired JSON output. Note that it does not manipulate the embedded struct's custom marshalling.
Alternatively, one can use a reflection-based approach:
func (e *Employee) MarshalJSON() ([]byte, error) { v := reflect.ValueOf(e).Elem() vf := v.FieldByName("Person") tmp, err := json.MarshalIndent(vf.Interface(), "", " ") if err != nil { return nil, err } return []byte(strings.Replace(string(tmp), "}", `,"jobRole":"`+e.JobRole+`"}`, -1)), nil }
This method uses reflection to access the embedded struct's value and field, enabling custom marshalling without relying on structural knowledge.
By implementing MarshalJSON() on the outer type, this approach ensures that both the embedded and outer struct's fields are marshaled correctly, preserving the desired output.
The above is the detailed content of How to Preserve Custom Marshalling in Go Embedded Structs?. For more information, please follow other related articles on the PHP Chinese website!