diff --git a/feature_reflect_native.go b/feature_reflect_native.go index 6844760..9bd290b 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -331,7 +331,9 @@ type boolCodec struct { } func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - *((*bool)(ptr)) = iter.ReadBool() + if !iter.ReadNil() { + *((*bool)(ptr)) = iter.ReadBool() + } } func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) { @@ -350,6 +352,10 @@ type emptyInterfaceCodec struct { } func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.ReadNil() { + *((*interface{})(ptr)) = nil + return + } existing := *((*interface{})(ptr)) if existing != nil && reflect.TypeOf(existing).Kind() == reflect.Ptr { iter.ReadVal(existing) diff --git a/jsoniter_bool_test.go b/jsoniter_bool_test.go index 5f6a824..5a9aa75 100644 --- a/jsoniter_bool_test.go +++ b/jsoniter_bool_test.go @@ -82,3 +82,32 @@ func Test_decode_string_bool(t *testing.T) { err = Unmarshal([]byte(`{"Field":true}`), &obj) should.NotNil(err) } + +func Test_bool_can_be_null(t *testing.T) { + type TestData struct { + Field bool `json:"field"` + } + should := require.New(t) + + obj := TestData{} + data1 := []byte(`{"field": true}`) + err := Unmarshal(data1, &obj) + should.Equal(nil, err) + should.Equal(true, obj.Field) + + data2 := []byte(`{"field": null}`) + err = Unmarshal(data2, &obj) + should.Equal(nil, 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.Equal(true, obj2.Field) + + err = json.Unmarshal(data2, &obj2) + should.Equal(nil, err) + should.Equal(true, obj2.Field) +} diff --git a/jsoniter_interface_test.go b/jsoniter_interface_test.go index f9d368b..0e7ef88 100644 --- a/jsoniter_interface_test.go +++ b/jsoniter_interface_test.go @@ -3,9 +3,10 @@ package jsoniter import ( "encoding/json" "fmt" - "github.com/stretchr/testify/require" "testing" "unsafe" + + "github.com/stretchr/testify/require" ) func Test_write_array_of_interface(t *testing.T) { @@ -313,3 +314,40 @@ func Test_unmarshal_ptr_to_interface(t *testing.T) { should.Nil(err) should.Equal("&{value}", fmt.Sprintf("%v", obj)) } + +func Test_nil_out_null_interface(t *testing.T) { + type TestData struct { + Field interface{} `json:"field"` + } + should := require.New(t) + + var boolVar bool + obj := TestData{ + Field: &boolVar, + } + + data1 := []byte(`{"field": true}`) + + err := Unmarshal(data1, &obj) + should.Equal(nil, err) + should.Equal(true, *(obj.Field.(*bool))) + + data2 := []byte(`{"field": null}`) + + err = Unmarshal(data2, &obj) + should.Equal(nil, err) + should.Equal(nil, obj.Field) + + // Checking stdlib behavior matches. + obj2 := TestData{ + Field: &boolVar, + } + + err = json.Unmarshal(data1, &obj2) + should.Equal(nil, err) + should.Equal(true, *(obj2.Field.(*bool))) + + err = json.Unmarshal(data2, &obj2) + should.Equal(nil, err) + should.Equal(nil, obj2.Field) +}