Support interface{} type for decoding

This commit is contained in:
Masaaki Goshima 2020-05-08 15:13:30 +09:00
parent 09bf666c9a
commit 9fe0063679
3 changed files with 174 additions and 1 deletions

View File

@ -7,6 +7,8 @@ import (
"strings"
"sync"
"unsafe"
"golang.org/x/xerrors"
)
// A Token holds a value of one of these types:
@ -155,6 +157,8 @@ func (d *Decoder) compile(typ *rtype) (decoder, error) {
return d.compileArray(typ)
case reflect.Map:
return d.compileMap(typ)
case reflect.Interface:
return d.compileInterface(typ)
case reflect.Int:
return d.compileInt()
case reflect.Int8:
@ -184,7 +188,7 @@ func (d *Decoder) compile(typ *rtype) (decoder, error) {
case reflect.Float64:
return d.compileFloat64()
}
return nil, nil
return nil, xerrors.Errorf("unknown type %s", typ)
}
func (d *Decoder) compilePtr(typ *rtype) (decoder, error) {
@ -305,6 +309,10 @@ func (d *Decoder) compileMap(typ *rtype) (decoder, error) {
return newMapDecoder(typ, keyDec, valueDec), nil
}
func (d *Decoder) compileInterface(typ *rtype) (decoder, error) {
return newInterfaceDecoder(typ), nil
}
func (d *Decoder) getTag(field reflect.StructField) string {
return field.Tag.Get("json")
}

124
decode_interface.go Normal file
View File

@ -0,0 +1,124 @@
package json
import (
"errors"
"reflect"
"unsafe"
)
type interfaceDecoder struct {
typ *rtype
dummy unsafe.Pointer // for escape value
}
func newInterfaceDecoder(typ *rtype) *interfaceDecoder {
return &interfaceDecoder{typ: typ}
}
var (
interfaceMapType = type2rtype(
reflect.TypeOf((*map[interface{}]interface{})(nil)).Elem(),
)
)
func (d *interfaceDecoder) decode(buf []byte, cursor int, p uintptr) (int, error) {
cursor = skipWhiteSpace(buf, cursor)
switch buf[cursor] {
case '{':
var v map[interface{}]interface{}
ptr := unsafe.Pointer(&v)
d.dummy = ptr
dec := newMapDecoder(interfaceMapType, newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ))
cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil {
return 0, err
}
*(*interface{})(unsafe.Pointer(p)) = v
return cursor, nil
case '[':
var v []interface{}
ptr := unsafe.Pointer(&v)
d.dummy = ptr // escape ptr
dec := newSliceDecoder(newInterfaceDecoder(d.typ), d.typ, d.typ.Size())
cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil {
return 0, err
}
*(*interface{})(unsafe.Pointer(p)) = v
return cursor, nil
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return newFloatDecoder(func(p uintptr, v float64) {
*(*interface{})(unsafe.Pointer(p)) = v
}).decode(buf, cursor, p)
case '"':
cursor++
start := cursor
for {
switch buf[cursor] {
case '\\':
cursor++
case '"':
literal := buf[start:cursor]
cursor++
*(*interface{})(unsafe.Pointer(p)) = *(*string)(unsafe.Pointer(&literal))
return cursor, nil
case '\000':
return 0, errors.New("unexpected error string")
}
cursor++
}
return 0, errors.New("unexpected error string")
case 't':
if cursor+3 >= len(buf) {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+1] != 'r' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+2] != 'u' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+3] != 'e' {
return 0, errors.New("unexpected error. invalid bool character")
}
cursor += 4
*(*interface{})(unsafe.Pointer(p)) = true
return cursor, nil
case 'f':
if cursor+4 >= len(buf) {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+1] != 'a' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+2] != 'l' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+3] != 's' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+4] != 'e' {
return 0, errors.New("unexpected error. invalid bool character")
}
cursor += 5
*(*interface{})(unsafe.Pointer(p)) = false
return cursor, nil
case 'n':
if cursor+3 >= len(buf) {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+1] != 'u' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+2] != 'l' {
return 0, errors.New("unexpected error. invalid bool character")
}
if buf[cursor+3] != 'l' {
return 0, errors.New("unexpected error. invalid bool character")
}
cursor += 4
*(*interface{})(unsafe.Pointer(p)) = nil
return cursor, nil
}
return cursor, errors.New("unexpected error value")
}

View File

@ -2,6 +2,7 @@ package json_test
import (
"fmt"
"reflect"
"testing"
"github.com/goccy/go-json"
@ -134,4 +135,44 @@ func Test_Decoder(t *testing.T) {
assertEq(t, "struct.D.BB", "world", v.D.BB)
assertEq(t, "struct.D.CC", true, v.D.CC)
})
t.Run("interface", func(t *testing.T) {
t.Run("number", func(t *testing.T) {
var v interface{}
assertErr(t, json.Unmarshal([]byte(`10`), &v))
assertEq(t, "interface.kind", "float64", reflect.TypeOf(v).Kind().String())
assertEq(t, "interface", `10`, fmt.Sprint(v))
})
t.Run("string", func(t *testing.T) {
var v interface{}
assertErr(t, json.Unmarshal([]byte(`"hello"`), &v))
assertEq(t, "interface.kind", "string", reflect.TypeOf(v).Kind().String())
assertEq(t, "interface", `hello`, fmt.Sprint(v))
})
t.Run("bool", func(t *testing.T) {
var v interface{}
assertErr(t, json.Unmarshal([]byte(`true`), &v))
assertEq(t, "interface.kind", "bool", reflect.TypeOf(v).Kind().String())
assertEq(t, "interface", `true`, fmt.Sprint(v))
})
t.Run("slice", func(t *testing.T) {
var v interface{}
assertErr(t, json.Unmarshal([]byte(`[1,2,3,4]`), &v))
assertEq(t, "interface.kind", "slice", reflect.TypeOf(v).Kind().String())
assertEq(t, "interface", `[1 2 3 4]`, fmt.Sprint(v))
})
t.Run("map", func(t *testing.T) {
var v interface{}
assertErr(t, json.Unmarshal([]byte(`{"a": 1, "b": "c"}`), &v))
assertEq(t, "interface.kind", "map", reflect.TypeOf(v).Kind().String())
m := v.(map[interface{}]interface{})
assertEq(t, "interface", `1`, fmt.Sprint(m["a"]))
assertEq(t, "interface", `c`, fmt.Sprint(m["b"]))
})
t.Run("null", func(t *testing.T) {
var v interface{}
v = 1
assertErr(t, json.Unmarshal([]byte(`null`), &v))
assertEq(t, "interface", nil, v)
})
})
}