Skip to content

Commit 18a241d

Browse files
committed
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.
1 parent 0fdf883 commit 18a241d

File tree

3 files changed

+75
-2
lines changed

3 files changed

+75
-2
lines changed

feature_reflect_native.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,9 @@ type boolCodec struct {
331331
}
332332

333333
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
334-
*((*bool)(ptr)) = iter.ReadBool()
334+
if !iter.ReadNil() {
335+
*((*bool)(ptr)) = iter.ReadBool()
336+
}
335337
}
336338

337339
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@@ -350,6 +352,10 @@ type emptyInterfaceCodec struct {
350352
}
351353

352354
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
355+
if iter.ReadNil() {
356+
*((*interface{})(ptr)) = nil
357+
return
358+
}
353359
existing := *((*interface{})(ptr))
354360
if existing != nil && reflect.TypeOf(existing).Kind() == reflect.Ptr {
355361
iter.ReadVal(existing)

jsoniter_bool_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,32 @@ func Test_decode_string_bool(t *testing.T) {
8282
err = Unmarshal([]byte(`{"Field":true}`), &obj)
8383
should.NotNil(err)
8484
}
85+
86+
func Test_bool_can_be_null(t *testing.T) {
87+
type TestData struct {
88+
Field bool `json:"field"`
89+
}
90+
should := require.New(t)
91+
92+
obj := TestData{}
93+
data1 := []byte(`{"field": true}`)
94+
err := Unmarshal(data1, &obj)
95+
should.Equal(nil, err)
96+
should.Equal(true, obj.Field)
97+
98+
data2 := []byte(`{"field": null}`)
99+
err = Unmarshal(data2, &obj)
100+
should.Equal(nil, err)
101+
// Same behavior as stdlib, not touching the existing value.
102+
should.Equal(true, obj.Field)
103+
104+
// Checking stdlib behavior as well
105+
obj2 := TestData{}
106+
err = json.Unmarshal(data1, &obj2)
107+
should.Equal(nil, err)
108+
should.Equal(true, obj2.Field)
109+
110+
err = json.Unmarshal(data2, &obj2)
111+
should.Equal(nil, err)
112+
should.Equal(true, obj2.Field)
113+
}

jsoniter_interface_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package jsoniter
33
import (
44
"encoding/json"
55
"fmt"
6-
"github.com/stretchr/testify/require"
76
"testing"
87
"unsafe"
8+
9+
"github.com/stretchr/testify/require"
910
)
1011

1112
func Test_write_array_of_interface(t *testing.T) {
@@ -313,3 +314,40 @@ func Test_unmarshal_ptr_to_interface(t *testing.T) {
313314
should.Nil(err)
314315
should.Equal("&{value}", fmt.Sprintf("%v", obj))
315316
}
317+
318+
func Test_nil_out_null_interface(t *testing.T) {
319+
type TestData struct {
320+
Field interface{} `json:"field"`
321+
}
322+
should := require.New(t)
323+
324+
var boolVar bool
325+
obj := TestData{
326+
Field: &boolVar,
327+
}
328+
329+
data1 := []byte(`{"field": true}`)
330+
331+
err := Unmarshal(data1, &obj)
332+
should.Equal(nil, err)
333+
should.Equal(true, *(obj.Field.(*bool)))
334+
335+
data2 := []byte(`{"field": null}`)
336+
337+
err = Unmarshal(data2, &obj)
338+
should.Equal(nil, err)
339+
should.Equal(nil, obj.Field)
340+
341+
// Checking stdlib behavior matches.
342+
obj2 := TestData{
343+
Field: &boolVar,
344+
}
345+
346+
err = json.Unmarshal(data1, &obj2)
347+
should.Equal(nil, err)
348+
should.Equal(true, *(obj2.Field.(*bool)))
349+
350+
err = json.Unmarshal(data2, &obj2)
351+
should.Equal(nil, err)
352+
should.Equal(nil, obj2.Field)
353+
}

0 commit comments

Comments
 (0)