In the example below, marshalled struct did not follow the struct shape because we override the marshalling process by defining MarshalJSON(). When we call json.Marshal(), the package checks if the value implements Marshaller, which defines MarshalJSON(). Inside the custom MarshalJSON, we define another struct, Alias, to transform the final shape of the marshalled value.
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
// MarshalJSON overrides default JSON marshaling
func (p Person) MarshalJSON() ([]byte, error) {
// Create an anonymous struct for custom output
type Alias struct {
Name string `json:"name"`
IsAdult bool `json:"is_adult"`
}
return json.Marshal(Alias{
Name: p.Name,
IsAdult: p.Age >= 18, // derived field
})
}
func main() {
p := Person{
Name: "alice",
Age: 20,
}
data, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(data)) // {"name":"alice","is_adult":true}
}