diff --git a/config.go b/config.go index 2adcdc3b..b06744e9 100644 --- a/config.go +++ b/config.go @@ -25,6 +25,7 @@ type Config struct { ValidateJsonRawMessage bool ObjectFieldMustBeSimpleString bool CaseSensitive bool + EmptyCollections bool } // API the public interface of this package. @@ -80,6 +81,7 @@ type frozenConfig struct { streamPool *sync.Pool iteratorPool *sync.Pool caseSensitive bool + emptyCollections bool } func (cfg *frozenConfig) initCache() { @@ -134,6 +136,7 @@ func (cfg Config) Froze() API { onlyTaggedField: cfg.OnlyTaggedField, disallowUnknownFields: cfg.DisallowUnknownFields, caseSensitive: cfg.CaseSensitive, + emptyCollections: cfg.EmptyCollections, } api.streamPool = &sync.Pool{ New: func() interface{} { diff --git a/misc_tests/jsoniter_array_test.go b/misc_tests/jsoniter_array_test.go index ef60420d..c9de2fda 100644 --- a/misc_tests/jsoniter_array_test.go +++ b/misc_tests/jsoniter_array_test.go @@ -226,6 +226,23 @@ func Test_decode_large_slice(t *testing.T) { should.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, slice) } +func Test_encode_nil_slice(t *testing.T) { + should := require.New(t) + var nilSlice []string + output, err := jsoniter.MarshalToString(nilSlice) + should.NoError(err) + should.Equal(`null`, output) +} + +func Test_encode_nil_as_empty_slice(t *testing.T) { + should := require.New(t) + json := jsoniter.Config{EmptyCollections: true}.Froze() + var nilSlice []string + output, err := json.MarshalToString(nilSlice) + should.NoError(err) + should.Equal(`[]`, output) +} + func Benchmark_jsoniter_array(b *testing.B) { b.ReportAllocs() input := []byte(`[1,2,3,4,5,6,7,8,9]`) diff --git a/misc_tests/jsoniter_map_test.go b/misc_tests/jsoniter_map_test.go index b73de698..5d4e4c69 100644 --- a/misc_tests/jsoniter_map_test.go +++ b/misc_tests/jsoniter_map_test.go @@ -50,3 +50,12 @@ func Test_encode_nil_map(t *testing.T) { should.NoError(err) should.Equal(`null`, output) } + +func Test_encode_nil_as_empty_map(t *testing.T) { + should := require.New(t) + json := jsoniter.Config{EmptyCollections: true}.Froze() + var nilMap map[string]string + output, err := json.MarshalToString(nilMap) + should.NoError(err) + should.Equal(`{}`, output) +} diff --git a/reflect_map.go b/reflect_map.go index 4e479c8a..9c9c682e 100644 --- a/reflect_map.go +++ b/reflect_map.go @@ -27,15 +27,17 @@ func encoderOfMap(ctx *ctx, typ reflect2.Type) ValEncoder { mapType := typ.(*reflect2.UnsafeMapType) if ctx.sortMapKeys { return &sortKeysMapEncoder{ - mapType: mapType, - keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), - elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), + mapType: mapType, + keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), + elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), + emptyCollections: ctx.emptyCollections, } } return &mapEncoder{ - mapType: mapType, - keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), - elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), + mapType: mapType, + keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), + elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), + emptyCollections: ctx.emptyCollections, } } @@ -246,13 +248,18 @@ func (encoder *dynamicMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool { } type mapEncoder struct { - mapType *reflect2.UnsafeMapType - keyEncoder ValEncoder - elemEncoder ValEncoder + mapType *reflect2.UnsafeMapType + keyEncoder ValEncoder + elemEncoder ValEncoder + emptyCollections bool } func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { if *(*unsafe.Pointer)(ptr) == nil { + if encoder.emptyCollections { + stream.WriteEmptyObject() + return + } stream.WriteNil() return } @@ -280,13 +287,18 @@ func (encoder *mapEncoder) IsEmpty(ptr unsafe.Pointer) bool { } type sortKeysMapEncoder struct { - mapType *reflect2.UnsafeMapType - keyEncoder ValEncoder - elemEncoder ValEncoder + mapType *reflect2.UnsafeMapType + keyEncoder ValEncoder + elemEncoder ValEncoder + emptyCollections bool } func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { if *(*unsafe.Pointer)(ptr) == nil { + if encoder.emptyCollections { + stream.WriteEmptyObject() + return + } stream.WriteNil() return } diff --git a/reflect_slice.go b/reflect_slice.go index 9441d79d..7c6d989d 100644 --- a/reflect_slice.go +++ b/reflect_slice.go @@ -16,16 +16,21 @@ func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder { func encoderOfSlice(ctx *ctx, typ reflect2.Type) ValEncoder { sliceType := typ.(*reflect2.UnsafeSliceType) encoder := encoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) - return &sliceEncoder{sliceType, encoder} + return &sliceEncoder{sliceType, encoder, ctx.emptyCollections} } type sliceEncoder struct { - sliceType *reflect2.UnsafeSliceType - elemEncoder ValEncoder + sliceType *reflect2.UnsafeSliceType + elemEncoder ValEncoder + emptyCollections bool } func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { if encoder.sliceType.UnsafeIsNil(ptr) { + if encoder.emptyCollections { + stream.WriteEmptyArray() + return + } stream.WriteNil() return }