Skip to content

Commit

Permalink
fix(omitempty): must omit empty slices
Browse files Browse the repository at this point in the history
The `omitempty` tag was omitting `nil` slices but not slices that were
allocated but had length zero. This has been fixes for consistency with
json.Marshal. Also empty structs are no longer omitted for consistency
with json.Marhsal.
  • Loading branch information
blgm committed May 22, 2020
1 parent 8367b06 commit 71749cb
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 17 deletions.
16 changes: 15 additions & 1 deletion marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func marshalStruct(ctx context.Context, in reflect.Value) (map[string]interface{

if public(f) {
p := path.ComputePath(f)
shouldSkip := p.OmitAlways || (p.OmitEmpty && in.Field(i).IsZero())
shouldSkip := p.OmitAlways || (p.OmitEmpty && isEmpty(in.Field(i)))

if !shouldSkip {
r, err := marshal(ctx.WithField(f.Name, f.Type), in.Field(i))
Expand Down Expand Up @@ -138,3 +138,17 @@ func marshalJSONMarshaler(ctx context.Context, in reflect.Value) (interface{}, e

return r, nil
}

func isEmpty(v reflect.Value) bool {
k := v.Kind()
switch {
case k == reflect.Interface, k == reflect.Ptr:
return v.IsZero() || v.IsNil()
case k == reflect.String, k == reflect.Map, k == reflect.Slice, k == reflect.Array:
return v.Len() == 0
case basicType(k):
return v.IsZero()
default:
return false
}
}
85 changes: 69 additions & 16 deletions marshal_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jsonry_test

import (
"encoding/json"
"errors"

"code.cloudfoundry.org/jsonry"
Expand Down Expand Up @@ -209,50 +210,102 @@ var _ = Describe("Marshal", func() {
})

Describe("omitempty", func() {
It("omits zero values of basic types", func() {
It("reads the `omitempty` field from JSON and JSONry tags with and without names", func() {
s := struct {
A string `json:",omitempty"`
B string `json:"bee,omitempty"`
C string `jsonry:",omitempty"`
D string `jsonry:"dee,omitempty"`
E string
}{}
expectToMarshal(s, `{"E":""}`)
expectToMarshal(s, `{}`)
})

// and any empty array, slice, map, or string.

It("omits false", func() {
s := struct {
A bool `jsonry:",omitempty"`
}{}
expectToMarshal(s, `{}`)
})

It("omits 0", func() {
s := struct {
A int `jsonry:",omitempty"`
B uint `jsonry:",omitempty"`
C float64 `jsonry:",omitempty"`
}{}
expectToMarshal(s, `{}`)
})

It("omits zero value structs", func() {
It("omits nil pointers", func() {
type t struct{ A string }
s := struct {
B t `jsonry:",omitempty"`
A *string `jsonry:",omitempty"`
B *t `jsonry:",omitempty"`
C *[]string `jsonry:",omitempty"`
D *map[int]int `jsonry:",omitempty"`
E *interface{} `jsonry:",omitempty"`
}{}
expectToMarshal(s, `{}`)
})

It("omits empty lists", func() {
It("omits nil interface values", func() {
s := struct {
A []string `jsonry:",omitempty"`
D [0]string `jsonry:",omitempty"`
A interface{} `jsonry:",omitempty"`
B json.Marshaler `jsonry:",omitempty"`
}{}
expectToMarshal(s, `{}`)
})

It("omits empty arrays", func() {
s := struct {
A [0]int `jsonry:",omitempty"`
}{A: [0]int{}}
expectToMarshal(s, `{}`)
})

It("omits empty slices", func() {
s := struct {
A []int `jsonry:",omitempty"`
B []int `jsonry:",omitempty"`
C []int `jsonry:",omitempty"`
}{
B: []int{},
C: make([]int, 0, 1),
}
expectToMarshal(s, `{}`)
})

It("omits empty maps", func() {
s := struct {
A map[interface{}]interface{} `jsonry:",omitempty"`
D map[int]int `jsonry:",omitempty"`
}{}
}{
D: make(map[int]int),
}
expectToMarshal(s, `{}`)
})

It("omits nil pointers", func() {
It("omits empty strings", func() {
s := struct {
A *string `json:",omitempty"`
B *string `json:"bee,omitempty"`
C *string `jsonry:",omitempty"`
D *string `jsonry:"dee,omitempty"`
E *string
A string `jsonry:",omitempty"`
B string `jsonry:",omitempty"`
}{
B: "",
}
expectToMarshal(s, `{}`)
})

// For consistency with json.Marshal, see https://github.com/golang/go/issues/11939
It("does not omit empty structs", func() {
type t struct {
A string `jsonry:",omitempty"`
}
s := struct {
B t `jsonry:",omitempty"`
}{}
expectToMarshal(s, `{"E":null}`)
expectToMarshal(s, `{"B":{}}`)
})
})

Expand Down

0 comments on commit 71749cb

Please sign in to comment.