mirror of https://github.com/goccy/go-json.git
Support interface{} type for decoding
This commit is contained in:
parent
09bf666c9a
commit
9fe0063679
10
decode.go
10
decode.go
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue