Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

golang json marshal: how to omit empty nested struct

Tags:

json

go

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"` } 
like image 890
Zhe Hu Avatar asked Oct 31 '15 02:10

Zhe Hu


2 Answers

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) } 
like image 152
Amit Kumar Gupta Avatar answered Sep 20 '22 01:09

Amit Kumar Gupta


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

like image 20
sepetrov Avatar answered Sep 21 '22 01:09

sepetrov