Pārlūkot izejas kodu

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 gadi atpakaļ
vecāks
revīzija
18a241d40b
3 mainītis faili ar 75 papildinājumiem un 2 dzēšanām
  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)
+}