Merge pull request #9 from goccy/feature/fix-null-values

Fix handling of null value for some types
This commit is contained in:
Masaaki Goshima 2020-08-08 13:23:32 +09:00 committed by GitHub
commit 1aad6b6bfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 5 deletions

View File

@ -20,6 +20,11 @@ func (d *arrayDecoder) decodeStream(s *stream, p uintptr) error {
for { for {
switch s.char() { switch s.char() {
case ' ', '\n', '\t', '\r': case ' ', '\n', '\t', '\r':
case 'n':
if err := nullBytes(s); err != nil {
return err
}
return nil
case '[': case '[':
idx := 0 idx := 0
for { for {
@ -63,6 +68,22 @@ func (d *arrayDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error
switch buf[cursor] { switch buf[cursor] {
case ' ', '\n', '\t', '\r': case ' ', '\n', '\t', '\r':
continue continue
case 'n':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
}
cursor += 4
return cursor, nil
case '[': case '[':
idx := 0 idx := 0
for { for {

View File

@ -47,7 +47,14 @@ func (d *mapDecoder) setValueStream(s *stream, key interface{}) error {
func (d *mapDecoder) decodeStream(s *stream, p uintptr) error { func (d *mapDecoder) decodeStream(s *stream, p uintptr) error {
s.skipWhiteSpace() s.skipWhiteSpace()
if s.char() != '{' { switch s.char() {
case 'n':
if err := nullBytes(s); err != nil {
return err
}
return nil
case '{':
default:
return errExpected("{ character for map value", s.totalOffset()) return errExpected("{ character for map value", s.totalOffset())
} }
mapValue := makemap(d.mapType, 0) mapValue := makemap(d.mapType, 0)
@ -94,7 +101,24 @@ func (d *mapDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error)
if buflen < 2 { if buflen < 2 {
return 0, errExpected("{} for map", cursor) return 0, errExpected("{} for map", cursor)
} }
if buf[cursor] != '{' { switch buf[cursor] {
case 'n':
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
}
cursor += 4
return cursor, nil
case '{':
default:
return 0, errExpected("{ character for map value", cursor) return 0, errExpected("{ character for map value", cursor)
} }
cursor++ cursor++

View File

@ -62,6 +62,11 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error {
case ' ', '\n', '\t', '\r': case ' ', '\n', '\t', '\r':
s.cursor++ s.cursor++
continue continue
case 'n':
if err := nullBytes(s); err != nil {
return err
}
return nil
case '[': case '[':
idx := 0 idx := 0
slice := d.newSlice() slice := d.newSlice()
@ -136,6 +141,22 @@ func (d *sliceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error
switch buf[cursor] { switch buf[cursor] {
case ' ', '\n', '\t', '\r': case ' ', '\n', '\t', '\r':
continue continue
case 'n':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
}
cursor += 4
return cursor, nil
case '[': case '[':
idx := 0 idx := 0
slice := d.newSlice() slice := d.newSlice()

View File

@ -135,12 +135,26 @@ func Test_Decoder(t *testing.T) {
assertEq(t, "struct.D.AA", 2, v.D.AA) assertEq(t, "struct.D.AA", 2, v.D.AA)
assertEq(t, "struct.D.BB", "world", v.D.BB) assertEq(t, "struct.D.BB", "world", v.D.BB)
assertEq(t, "struct.D.CC", true, v.D.CC) assertEq(t, "struct.D.CC", true, v.D.CC)
t.Run("struct.null", func(t *testing.T) { t.Run("struct.field null", func(t *testing.T) {
var v struct { var v struct {
A string A string
B []string
C []int
D map[string]interface{}
E [2]string
F interface{}
} }
assertErr(t, json.Unmarshal([]byte(`{"a":null}`), &v)) assertErr(t, json.Unmarshal([]byte(`{"a":null,"b":null,"c":null,"d":null,"e":null,"f":null}`), &v))
assertEq(t, "string is null", v.A, "") assertEq(t, "string", v.A, "")
assertNeq(t, "[]string", v.B, nil)
assertEq(t, "[]string", len(v.B), 0)
assertNeq(t, "[]int", v.C, nil)
assertEq(t, "[]int", len(v.C), 0)
assertNeq(t, "map", v.D, nil)
assertEq(t, "map", len(v.D), 0)
assertNeq(t, "array", v.E, nil)
assertEq(t, "array", len(v.E), 2)
assertEq(t, "interface{}", v.F, nil)
}) })
}) })
t.Run("interface", func(t *testing.T) { t.Run("interface", func(t *testing.T) {

View File

@ -15,3 +15,10 @@ func assertEq(t *testing.T, msg string, exp interface{}, act interface{}) {
t.Fatalf("failed to test for %s. exp=[%v] but act=[%v]", msg, exp, act) t.Fatalf("failed to test for %s. exp=[%v] but act=[%v]", msg, exp, act)
} }
} }
func assertNeq(t *testing.T, msg string, exp interface{}, act interface{}) {
t.Helper()
if exp == act {
t.Fatalf("failed to test for %s. expected value is not [%v] but got same value", msg, act)
}
}