From 640251ab91577282310b01a9853abbffd66e4fa9 Mon Sep 17 00:00:00 2001
From: Peng Gao <peng.gao.dut@gmail.com>
Date: Fri, 27 Oct 2017 00:27:58 +0800
Subject: [PATCH] Fix standard compatiblility

Non-nil but empty map with omitempty should be ignored.

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
---
 compatible_test.go           | 20 ++++++++++++++++++++
 feature_reflect.go           | 22 ++++++++++++++++++++--
 feature_reflect_extension.go |  5 +++--
 3 files changed, 43 insertions(+), 4 deletions(-)

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{