Selaa lähdekoodia

Allow null booleans

Make sure we do the same thing as stdlib with null booleans by not
touching the original value and discarding the null.

Another somewhat related change is nulling out null interface values in
the original structure. This also matches stdlib behavior.
Oleg Shaldybin 8 vuotta sitten
vanhempi
commit
18a241d40b
3 muutettua tiedostoa jossa 75 lisäystä ja 2 poistoa
  1. 7 1
      feature_reflect_native.go
  2. 29 0
      jsoniter_bool_test.go
  3. 39 1
      jsoniter_interface_test.go

+ 7 - 1
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)

+ 29 - 0
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)
+}

+ 39 - 1
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)
+}