Skip to content

Commit 71749cb

Browse files
committed
fix(omitempty): must omit empty slices
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.
1 parent 8367b06 commit 71749cb

2 files changed

Lines changed: 84 additions & 17 deletions

File tree

marshal.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func marshalStruct(ctx context.Context, in reflect.Value) (map[string]interface{
4444

4545
if public(f) {
4646
p := path.ComputePath(f)
47-
shouldSkip := p.OmitAlways || (p.OmitEmpty && in.Field(i).IsZero())
47+
shouldSkip := p.OmitAlways || (p.OmitEmpty && isEmpty(in.Field(i)))
4848

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

139139
return r, nil
140140
}
141+
142+
func isEmpty(v reflect.Value) bool {
143+
k := v.Kind()
144+
switch {
145+
case k == reflect.Interface, k == reflect.Ptr:
146+
return v.IsZero() || v.IsNil()
147+
case k == reflect.String, k == reflect.Map, k == reflect.Slice, k == reflect.Array:
148+
return v.Len() == 0
149+
case basicType(k):
150+
return v.IsZero()
151+
default:
152+
return false
153+
}
154+
}

marshal_test.go

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jsonry_test
22

33
import (
4+
"encoding/json"
45
"errors"
56

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

211212
Describe("omitempty", func() {
212-
It("omits zero values of basic types", func() {
213+
It("reads the `omitempty` field from JSON and JSONry tags with and without names", func() {
213214
s := struct {
214215
A string `json:",omitempty"`
215216
B string `json:"bee,omitempty"`
216217
C string `jsonry:",omitempty"`
217218
D string `jsonry:"dee,omitempty"`
218-
E string
219219
}{}
220-
expectToMarshal(s, `{"E":""}`)
220+
expectToMarshal(s, `{}`)
221+
})
222+
223+
// and any empty array, slice, map, or string.
224+
225+
It("omits false", func() {
226+
s := struct {
227+
A bool `jsonry:",omitempty"`
228+
}{}
229+
expectToMarshal(s, `{}`)
230+
})
231+
232+
It("omits 0", func() {
233+
s := struct {
234+
A int `jsonry:",omitempty"`
235+
B uint `jsonry:",omitempty"`
236+
C float64 `jsonry:",omitempty"`
237+
}{}
238+
expectToMarshal(s, `{}`)
221239
})
222240

223-
It("omits zero value structs", func() {
241+
It("omits nil pointers", func() {
224242
type t struct{ A string }
225243
s := struct {
226-
B t `jsonry:",omitempty"`
244+
A *string `jsonry:",omitempty"`
245+
B *t `jsonry:",omitempty"`
246+
C *[]string `jsonry:",omitempty"`
247+
D *map[int]int `jsonry:",omitempty"`
248+
E *interface{} `jsonry:",omitempty"`
227249
}{}
228250
expectToMarshal(s, `{}`)
229251
})
230252

231-
It("omits empty lists", func() {
253+
It("omits nil interface values", func() {
232254
s := struct {
233-
A []string `jsonry:",omitempty"`
234-
D [0]string `jsonry:",omitempty"`
255+
A interface{} `jsonry:",omitempty"`
256+
B json.Marshaler `jsonry:",omitempty"`
235257
}{}
236258
expectToMarshal(s, `{}`)
237259
})
238260

261+
It("omits empty arrays", func() {
262+
s := struct {
263+
A [0]int `jsonry:",omitempty"`
264+
}{A: [0]int{}}
265+
expectToMarshal(s, `{}`)
266+
})
267+
268+
It("omits empty slices", func() {
269+
s := struct {
270+
A []int `jsonry:",omitempty"`
271+
B []int `jsonry:",omitempty"`
272+
C []int `jsonry:",omitempty"`
273+
}{
274+
B: []int{},
275+
C: make([]int, 0, 1),
276+
}
277+
expectToMarshal(s, `{}`)
278+
})
279+
239280
It("omits empty maps", func() {
240281
s := struct {
241282
A map[interface{}]interface{} `jsonry:",omitempty"`
242283
D map[int]int `jsonry:",omitempty"`
243-
}{}
284+
}{
285+
D: make(map[int]int),
286+
}
244287
expectToMarshal(s, `{}`)
245288
})
246289

247-
It("omits nil pointers", func() {
290+
It("omits empty strings", func() {
248291
s := struct {
249-
A *string `json:",omitempty"`
250-
B *string `json:"bee,omitempty"`
251-
C *string `jsonry:",omitempty"`
252-
D *string `jsonry:"dee,omitempty"`
253-
E *string
292+
A string `jsonry:",omitempty"`
293+
B string `jsonry:",omitempty"`
294+
}{
295+
B: "",
296+
}
297+
expectToMarshal(s, `{}`)
298+
})
299+
300+
// For consistency with json.Marshal, see https://github.com/golang/go/issues/11939
301+
It("does not omit empty structs", func() {
302+
type t struct {
303+
A string `jsonry:",omitempty"`
304+
}
305+
s := struct {
306+
B t `jsonry:",omitempty"`
254307
}{}
255-
expectToMarshal(s, `{"E":null}`)
308+
expectToMarshal(s, `{"B":{}}`)
256309
})
257310
})
258311

0 commit comments

Comments
 (0)