I have a struct like this:
type Result struct { Data MyStruct `json:"data,omitempty"` Status string `json:"status,omitempty"` Reason string `json:"reason,omitempty"` }
But even if the instance of MyStruct is entirely empty (meaning, all values are default), it's being serialized as:
"data":{}
I know that the encoding/json docs specify that "empty" fields are:
false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero
but with no consideration for a struct with all empty/default values. All of its fields are also tagged with omitempty
, but this has no effect.
How can I get the JSON package to not marshal my field that is an empty struct?
As the docs say, "any nil pointer." -- make the struct a pointer. Pointers have obvious "empty" values: nil
.
Fix - define the type with a struct pointer field:
type Result struct { Data *MyStruct `json:"data,omitempty"` Status string `json:"status,omitempty"` Reason string `json:"reason,omitempty"` }
Then a value like this:
result := Result{}
Will marshal as:
{}
Explanation: Notice the *MyStruct
in our type definition. JSON serialization doesn't care whether it is a pointer or not -- that's a runtime detail. So making struct fields into pointers only has implications for compiling and runtime).
Just note that if you do change the field type from MyStruct
to *MyStruct
, you will need pointers to struct values to populate it, like so:
Data: &MyStruct{ /* values */ }
As @chakrit mentioned in a comment, you can't get this to work by implementing json.Marshaler
on MyStruct
, and implementing a custom JSON marshalling function on every struct that uses it can be a lot more work. It really depends on your use case as to whether it's worth the extra work or whether you're prepared to live with empty structs in your JSON, but here's the pattern I use applied to Result
:
type Result struct { Data MyStruct Status string Reason string } func (r Result) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Data *MyStruct `json:"data,omitempty"` Status string `json:"status,omitempty"` Reason string `json:"reason,omitempty"` }{ Data: &r.Data, Status: r.Status, Reason: r.Reason, }) } func (r *Result) UnmarshalJSON(b []byte) error { decoded := new(struct { Data *MyStruct `json:"data,omitempty"` Status string `json:"status,omitempty"` Reason string `json:"reason,omitempty"` }) err := json.Unmarshal(b, decoded) if err == nil { r.Data = decoded.Data r.Status = decoded.Status r.Reason = decoded.Reason } return err }
If you have huge structs with many fields this can become tedious, especially changing a struct's implementation later, but short of rewriting the whole json
package to suit your needs (not a good idea), this is pretty much the only way I can think of getting this done while still keeping a non-pointer MyStruct
in there.
Also, you don't have to use inline structs, you can create named ones. I use LiteIDE with code completion though, so I prefer inline to avoid clutter.
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