diff --git a/feature_reflect_native.go b/feature_reflect_native.go index cf939bf7..55e4d71d 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -391,15 +391,20 @@ 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 } 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) } @@ -660,7 +665,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 { 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 1e1fb833..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,10 +363,77 @@ 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) } + +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.NoError(err) + should.Equal("{}", string(js)) + + str, err := MarshalToString(obj) + should.NoError(err) + should.Equal(string(js), str) + + obj.Field = MyString("hello") + err = UnmarshalFromString(`{"field":null}`, &obj) + should.NoError(err) + should.Equal(nil, obj.Field) +} + +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.NoError(err) + should.Equal(`{"field":null}`, string(js)) + + str, err := MarshalToString(obj) + should.NoError(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.NoError(err) + should.Equal(`{"field":null}`, string(js)) + + str, err := MarshalToString(obj) + should.NoError(err) + should.Equal(string(js), str) + + obj.Field = MyString("hello") + err = Unmarshal(js, &obj) + should.NoError(err) + should.Equal(nil, obj.Field) +}