diff --git a/compatible_test.go b/compatible_test.go index bee3f94..4b725c5 100644 --- a/compatible_test.go +++ b/compatible_test.go @@ -18,3 +18,23 @@ func TestEncoderHasTrailingNewline(t *testing.T) { stdenc.Encode(1) should.Equal(stdbuf.Bytes(), buf.Bytes()) } + +// Non-nil but empty map should be ignored. +func TestOmitempty(t *testing.T) { + o := struct { + A string `json:"a,omitempty"` + B string `json:"b,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` + }{ + A: "a", + B: "b", + Annotations: map[string]string{}, + } + should := require.New(t) + var buf, stdbuf bytes.Buffer + enc := ConfigCompatibleWithStandardLibrary.NewEncoder(&buf) + enc.Encode(o) + stdenc := json.NewEncoder(&stdbuf) + stdenc.Encode(o) + should.Equal(string(stdbuf.Bytes()), string(buf.Bytes())) +} diff --git a/feature_reflect.go b/feature_reflect.go index 4483e34..bdd7e18 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -130,10 +130,28 @@ func (encoder *optionalEncoder) EncodeInterface(val interface{}, stream *Stream) } func (encoder *optionalEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return *((*unsafe.Pointer)(ptr)) == nil +} + +type optionalMapEncoder struct { + valueEncoder ValEncoder +} + +func (encoder *optionalMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { if *((*unsafe.Pointer)(ptr)) == nil { - return true + stream.WriteNil() + } else { + encoder.valueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) } - return false +} + +func (encoder *optionalMapEncoder) EncodeInterface(val interface{}, stream *Stream) { + WriteToStream(val, stream, encoder) +} + +func (encoder *optionalMapEncoder) IsEmpty(ptr unsafe.Pointer) bool { + p := *((*unsafe.Pointer)(ptr)) + return p == nil || encoder.valueEncoder.IsEmpty(p) } type placeholderEncoder struct { diff --git a/feature_reflect_extension.go b/feature_reflect_extension.go index 74f4b8b..b535624 100644 --- a/feature_reflect_extension.go +++ b/feature_reflect_extension.go @@ -280,9 +280,10 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err if len(fieldNames) > 0 && err != nil { return nil, err } - // map is stored as pointer in the struct + // map is stored as pointer in the struct, + // and treat nil or empty map as empty field if encoder != nil && field.Type.Kind() == reflect.Map { - encoder = &optionalEncoder{encoder} + encoder = &optionalMapEncoder{encoder} } } binding := &Binding{