From 2f7e5c8dd711b425301788919ab2e511b584073e Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:00:48 -0700 Subject: [PATCH 1/7] add failing tests for nil non-empty interfaces --- jsoniter_interface_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/jsoniter_interface_test.go b/jsoniter_interface_test.go index 1e1fb833..c4893321 100644 --- a/jsoniter_interface_test.go +++ b/jsoniter_interface_test.go @@ -370,3 +370,41 @@ func Test_omitempty_nil_interface(t *testing.T) { should.Equal(nil, err) should.Equal(string(js), str) } + +func Test_marshal_nil_marshaler_interface(t *testing.T) { + type TestData struct { + Field json.Marshaler `json:"field"` + } + should := require.New(t) + + obj := TestData{ + Field: nil, + } + + js, err := json.Marshal(obj) + should.Equal(nil, err) + should.Equal(`{"field":null}`, string(js)) + + str, err := MarshalToString(obj) + should.Equal(nil, err) + should.Equal(string(js), str) +} + +func Test_marshal_nil_nonempty_interface(t *testing.T) { + type TestData struct { + Field MyInterface `json:"field"` + } + should := require.New(t) + + obj := TestData{ + Field: nil, + } + + js, err := json.Marshal(obj) + should.Equal(nil, err) + should.Equal(`{"field":null}`, string(js)) + + str, err := MarshalToString(obj) + should.Equal(nil, err) + should.Equal(string(js), str) +} From ddc5af4512383889c15727f2e1b09e880cbeb2d3 Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:04:36 -0700 Subject: [PATCH 2/7] fix encoding of nil non-empty interface --- feature_reflect_native.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/feature_reflect_native.go b/feature_reflect_native.go index cf939bf7..6204138a 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -397,9 +397,11 @@ func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { nonEmptyInterface := (*nonEmptyInterface)(ptr) var i interface{} - e := (*emptyInterface)(unsafe.Pointer(&i)) - e.typ = nonEmptyInterface.itab.typ - e.word = nonEmptyInterface.word + if nonEmptyInterface.itab != nil { + e := (*emptyInterface)(unsafe.Pointer(&i)) + e.typ = nonEmptyInterface.itab.typ + e.word = nonEmptyInterface.word + } stream.WriteVal(i) } From 2017f3866bc0fac0047faeb1d33140cc967ba717 Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:08:32 -0700 Subject: [PATCH 3/7] fix encoding of nil marshaler interface --- feature_reflect_native.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/feature_reflect_native.go b/feature_reflect_native.go index 6204138a..920c568b 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -662,7 +662,11 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { templateInterface := encoder.templateInterface templateInterface.word = ptr realInterface := (*interface{})(unsafe.Pointer(&templateInterface)) - marshaler := (*realInterface).(json.Marshaler) + marshaler, ok := (*realInterface).(json.Marshaler) + if !ok { + stream.WriteVal(nil) + return + } bytes, err := marshaler.MarshalJSON() if err != nil { From 832437440218a83b4798e5c893d2151865390b8a Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:24:27 -0700 Subject: [PATCH 4/7] add tests for decoding nil interfaces --- jsoniter_interface_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/jsoniter_interface_test.go b/jsoniter_interface_test.go index c4893321..62aec84e 100644 --- a/jsoniter_interface_test.go +++ b/jsoniter_interface_test.go @@ -371,6 +371,29 @@ func Test_omitempty_nil_interface(t *testing.T) { should.Equal(string(js), str) } +func Test_omitempty_nil_nonempty_interface(t *testing.T) { + type TestData struct { + Field MyInterface `json:"field,omitempty"` + } + should := require.New(t) + + obj := TestData{ + Field: nil, + } + + js, err := json.Marshal(obj) + should.Equal(nil, err) + should.Equal("{}", string(js)) + + str, err := MarshalToString(obj) + should.Equal(nil, err) + should.Equal(string(js), str) + + err = Unmarshal(js, &obj) + should.Equal(nil, err) + should.Equal(nil, obj.Field) +} + func Test_marshal_nil_marshaler_interface(t *testing.T) { type TestData struct { Field json.Marshaler `json:"field"` @@ -407,4 +430,9 @@ func Test_marshal_nil_nonempty_interface(t *testing.T) { str, err := MarshalToString(obj) should.Equal(nil, err) should.Equal(string(js), str) + + obj.Field = MyString("hello") + err = Unmarshal(js, &obj) + should.Equal(nil, err) + should.Equal(nil, obj.Field) } From c59c42fda0df74132d6ef5c39914b127b5c16407 Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:24:55 -0700 Subject: [PATCH 5/7] fix decoding of nil non-empty interface --- feature_reflect_native.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/feature_reflect_native.go b/feature_reflect_native.go index 920c568b..55e4d71d 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -391,6 +391,9 @@ func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) e.typ = nonEmptyInterface.itab.typ e.word = nonEmptyInterface.word iter.ReadVal(&i) + if e.word == nil { + nonEmptyInterface.itab = nil + } nonEmptyInterface.word = e.word } From 48cc4d965a5284c5b16d2029766e6c0df7622a73 Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:27:32 -0700 Subject: [PATCH 6/7] improve test --- jsoniter_interface_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsoniter_interface_test.go b/jsoniter_interface_test.go index 62aec84e..9abfaa3f 100644 --- a/jsoniter_interface_test.go +++ b/jsoniter_interface_test.go @@ -389,7 +389,8 @@ func Test_omitempty_nil_nonempty_interface(t *testing.T) { should.Equal(nil, err) should.Equal(string(js), str) - err = Unmarshal(js, &obj) + obj.Field = MyString("hello") + err = UnmarshalFromString(`{"field":null}`, &obj) should.Equal(nil, err) should.Equal(nil, obj.Field) } From b5d2607a6dddce39b3aef65c44bc644d153b2c04 Mon Sep 17 00:00:00 2001 From: Jason Toffaletti Date: Sat, 16 Sep 2017 16:30:04 -0700 Subject: [PATCH 7/7] replace should.Equal(nil, err) with should.NoError(err) --- jsoniter_bool_test.go | 8 ++++---- jsoniter_enum_marshaler_test.go | 4 ++-- jsoniter_interface_test.go | 28 ++++++++++++++-------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/jsoniter_bool_test.go b/jsoniter_bool_test.go index 5a9aa759..461b88ba 100644 --- a/jsoniter_bool_test.go +++ b/jsoniter_bool_test.go @@ -92,22 +92,22 @@ func Test_bool_can_be_null(t *testing.T) { obj := TestData{} data1 := []byte(`{"field": true}`) err := Unmarshal(data1, &obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(true, obj.Field) data2 := []byte(`{"field": null}`) err = Unmarshal(data2, &obj) - should.Equal(nil, err) + should.NoError(err) // Same behavior as stdlib, not touching the existing value. should.Equal(true, obj.Field) // Checking stdlib behavior as well obj2 := TestData{} err = json.Unmarshal(data1, &obj2) - should.Equal(nil, err) + should.NoError(err) should.Equal(true, obj2.Field) err = json.Unmarshal(data2, &obj2) - should.Equal(nil, err) + should.NoError(err) should.Equal(true, obj2.Field) } diff --git a/jsoniter_enum_marshaler_test.go b/jsoniter_enum_marshaler_test.go index 4f9be8bb..69e1e8cd 100644 --- a/jsoniter_enum_marshaler_test.go +++ b/jsoniter_enum_marshaler_test.go @@ -40,11 +40,11 @@ func Test_custom_marshaler_on_enum(t *testing.T) { w := Wrapper{Payload: MyEnumB} jb, err := Marshal(w) - should.Equal(nil, err) + should.NoError(err) should.Equal(`{"Payload":"foo-1"}`, string(jb)) var w2 Wrapper2 err = Unmarshal(jb, &w2) - should.Equal(nil, err) + should.NoError(err) should.Equal(MyEnumB, w2.Payload) } diff --git a/jsoniter_interface_test.go b/jsoniter_interface_test.go index 9abfaa3f..a464f46b 100644 --- a/jsoniter_interface_test.go +++ b/jsoniter_interface_test.go @@ -329,13 +329,13 @@ func Test_nil_out_null_interface(t *testing.T) { data1 := []byte(`{"field": true}`) err := Unmarshal(data1, &obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(true, *(obj.Field.(*bool))) data2 := []byte(`{"field": null}`) err = Unmarshal(data2, &obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(nil, obj.Field) // Checking stdlib behavior matches. @@ -344,11 +344,11 @@ func Test_nil_out_null_interface(t *testing.T) { } err = json.Unmarshal(data1, &obj2) - should.Equal(nil, err) + should.NoError(err) should.Equal(true, *(obj2.Field.(*bool))) err = json.Unmarshal(data2, &obj2) - should.Equal(nil, err) + should.NoError(err) should.Equal(nil, obj2.Field) } @@ -363,11 +363,11 @@ func Test_omitempty_nil_interface(t *testing.T) { } js, err := json.Marshal(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal("{}", string(js)) str, err := MarshalToString(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(string(js), str) } @@ -382,16 +382,16 @@ func Test_omitempty_nil_nonempty_interface(t *testing.T) { } js, err := json.Marshal(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal("{}", string(js)) str, err := MarshalToString(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(string(js), str) obj.Field = MyString("hello") err = UnmarshalFromString(`{"field":null}`, &obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(nil, obj.Field) } @@ -406,11 +406,11 @@ func Test_marshal_nil_marshaler_interface(t *testing.T) { } js, err := json.Marshal(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(`{"field":null}`, string(js)) str, err := MarshalToString(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(string(js), str) } @@ -425,15 +425,15 @@ func Test_marshal_nil_nonempty_interface(t *testing.T) { } js, err := json.Marshal(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(`{"field":null}`, string(js)) str, err := MarshalToString(obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(string(js), str) obj.Field = MyString("hello") err = Unmarshal(js, &obj) - should.Equal(nil, err) + should.NoError(err) should.Equal(nil, obj.Field) }