go playground
As shown in the code above, one can use json:",omitempty"
to omit certain fields in a struct to appear in json.
For example
type ColorGroup struct { ID int `json:",omitempty"` Name string Colors []string } type Total struct { A ColorGroup`json:",omitempty"` B string`json:",omitempty"` } group := Total{ A: ColorGroup{}, }
In this case, B
won't show up in json.Marshal(group)
However, if
group := Total{ B:"abc", }
A
still shows up in json.Marshal(group)
{"A":{"Name":"","Colors":null},"B":"abc"}
Question is how do we get only
{"B":"abc"}
EDIT: After some googling, here is a suggestion use pointer, in other words, turn Total
into
type Total struct { A *ColorGroup`json:",omitempty"` B string`json:",omitempty"` }
From the documentation:
Struct values encode as JSON objects. Each exported struct field becomes a member of the object unless
- the field's tag is "-", or
- the field is empty and its tag specifies the "omitempty" option.
The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero.
In your declaration of group
, it's implicit that group.A
will be the zero value of the ColorGroup
struct type. And notice that zero-values-of-struct-types is not mentioned in that list of things that are considered "empty values".
As you found, the workaround for your case is to use a pointer. This will work if you don't specify A
in your declaration of group
. If you specify it to be a pointer to a zero-struct, then it will show up again.
playground link:
package main import ( "encoding/json" "fmt" "os" ) func main() { type colorGroup struct { ID int `json:",omitempty"` Name string Colors []string } type total struct { A *colorGroup `json:",omitempty"` B string `json:",omitempty"` } groupWithNilA := total{ B: "abc", } b, err := json.Marshal(groupWithNilA) if err != nil { fmt.Println("error:", err) } os.Stderr.Write(b) println() groupWithPointerToZeroA := total{ A: &colorGroup{}, B: "abc", } b, err = json.Marshal(groupWithPointerToZeroA) if err != nil { fmt.Println("error:", err) } os.Stderr.Write(b) }
This is an alternative solution, in case you would like to avoid using pointers to structs. The Container
struct implements json.Marshaller
, which allows us to decide which members of the strcut should be omitted.
https://play.golang.com/p/hMJbQ-QQ5PU
package main import ( "encoding/json" "fmt" ) func main() { for _, c := range []Container{ {}, { Element: KeyValue{ Key: "foo", Value: "bar", }, }, } { b, err := json.Marshal(c) if err != nil { panic(err) } fmt.Println(string(b)) } } type Container struct { Element KeyValue } func (c Container) MarshalJSON() ([]byte, error) { // Alias is an alias type of Container to avoid recursion. type Alias Container // AliasWithInterface wraps Alias and overrides the struct members, // which we want to omit if they are the zero value of the type. type AliasWithInterface struct { Alias Element interface{} `json:",omitempty"` } return json.Marshal(AliasWithInterface{ Alias: Alias(c), Element: c.Element.jsonValue(), }) } type KeyValue struct { Key string Value string } // jsonValue returns nil if kv is the zero value of KeyValue. It returns kv otherwise. func (kv KeyValue) jsonValue() interface{} { var zero KeyValue if kv == zero { return nil } return kv }
EDIT: added documentation
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