I have a struct in Golang which a slice of my alias type:
type Foo struct {
NextSet []House `json:"next_set"`
}
I want to marshal this into a json. When I do not initialise NextSet
I want to omit the next_set
key in the JSON.
However When I initialize it as empty []House{}
I want to show it as an empty array in my JSON: next_set: []
How can I do this? omitempty
tag does not work since it also omits when next_set
is []
4
encoding/json
doesn’t work like that. If you need specifically that behavior, implement it in a custom JSON marshaler.
This is how it could look like:
type Foo struct {
Bar string `json:"bar"`
NextSet []string `json:"next_set"`
}
func (f Foo) MarshalJSON() ([]byte, error) {
type myFoo Foo
if f.NextSet == nil {
// Exclude from JSON output
v := struct {
myFoo
NextSet int `json:"next_set,omitempty"`
}{myFoo: myFoo(f)}
return json.Marshal(v)
}
return json.Marshal(myFoo(f))
}
The idea is that we create a new (anonymous) struct in which we “shadow” the Foo.NextSet
field with another one (having same name) which we omit from the output using its zero value and omitempty
JSON option, but keep all other fields by embedding Foo
. The purpose of the myFoo
type is to create a new type stripping the Foo.MarshalJSON()
method to avoid endless “recursion” (to avoid stack overflow error).
Testing it:
data, err := json.Marshal(Foo{Bar: "bar"})
fmt.Println("When nil:", string(data), err)
data, err = json.Marshal(Foo{Bar: "bar", NextSet: []string{}})
fmt.Println("When empty:", string(data), err)
data, err = json.Marshal(Foo{Bar: "bar", NextSet: []string{"ns"}})
fmt.Println("When non-empty:", string(data), err)
Output (try it on the Go Playground):
When nil: {"bar":"bar"} <nil>
When empty: {"bar":"bar","next_set":[]} <nil>
When non-empty: {"bar":"bar","next_set":["ns"]} <nil>
Also note that a proposal has been accepted of an omitzero
JSON option (not yet in Go 1.23 but could make it in Go 1.24) which will achieve this without having to write custom marshaling logic.
The proposal is:
When marshaling, the “omitzero” option specifies that the struct field should be omitted if the field value is zero as determined by the “IsZero() bool” method if present, otherwise based on whether the field is the zero Go value (according to reflect.Value.IsZero). This option has no effect when unmarshaling. If “omitempty” is specified together with “omitzero”, whether a field is omitted is based on the logical OR of the two.
This will mean that omitzero of a slice omits a nil slice but emits [] for a zero-length non-nil slice (and similar for maps). It will also mean that omitzero of a time.Time omits time.Time{}. Neither of these strictly requires calling the IsZero method, but custom types may may find implementing IsZero useful.