Custom MarshalJSON in GOLang.
Getting JSON from structs or any interface{}
in GO is pretty trivial, but custom marshalling can be troublesome.
The following struct is based on that in the GO docs but with the json
field hints to demonstrate the output modifications produced.
type Message struct {
Name string `json:"user"`
Body string `json:"message"`
Time int64 `json:"timestamp"`
}
m := Message{"Alice", "Hello", 1294706395881547000}
b, err := json.Marshal(m)
b == {
"user":"Alice",
"message":"Hello",
"timestamp":1294706395881547000
}
The above shows how to rename fields during output, but modifying an output requires the a custom MarshalJSON
function on the struct. If this function exists, json.encode
will call it recurvisely on the struct and any interfaces within the struct. This is also helpful if you cannot modify the struct with the json
field hints.
This simple example performs the same modifications as the field hints above and adds the additional ID and Origin fields.
func (m *Message) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
ID string `json:"id"`
Origin string `json:"origin"`
Name string `json:"name"`
Message string `json:"message"`
Timestamp int64 `json:"timestamp"`
}{
ID: "Alices-Message-ID"
Origin: "Wonderland"
Name: m.Name,
Message: m.Body,
Timestamp: m.Time,
})
}
The problem with this solution is the requirement to specify each of the fields that exist on the Message struct. If we use both the json
field hints and the above approach we eliminate this issue.
func (m *Message) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
ID string `json:"id"`
Origin string `json:"origin"`
*Message
}{
ID: "Alices-Message-ID"
Origin: "Wonderland"
Message: m
})
}
However, the above function will loop; the outer struct
marshalling will call the inner Message
struct marshal function and there begins the loop. To circumvent this issue we must instead "copy" the original object; this object will have the same properties of the original but no functions - thus removing the infinite loop.
func (m *Message) MarshalJSON() ([]byte, error) {
type Copy Message
return json.Marshal(&struct {
ID string `json:"id"`
Origin string `json:"origin"`
*Copy
}{
ID: "Alices-Message-ID"
Origin: "Wonderland"
Copy: (*Copy)(m),
})
}