Merge pull request #257 from Kiraub/master

Add decoder for func type
This commit is contained in:
Masaaki Goshima 2021-06-24 11:59:55 +09:00 committed by GitHub
commit 397a4e45d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 163 additions and 2 deletions

View File

@ -152,6 +152,7 @@ func Test_Decoder(t *testing.T) {
B string `json:"str"` B string `json:"str"`
C bool C bool
D *T D *T
E func()
} }
content := []byte(` content := []byte(`
{ {
@ -162,7 +163,8 @@ func Test_Decoder(t *testing.T) {
"aa": 2, "aa": 2,
"bb": "world", "bb": "world",
"cc": true "cc": true
} },
"e" : null
}`) }`)
assertErr(t, json.Unmarshal(content, &v)) assertErr(t, json.Unmarshal(content, &v))
assertEq(t, "struct.A", 123, v.A) assertEq(t, "struct.A", 123, v.A)
@ -171,6 +173,7 @@ 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)
assertEq(t, "struct.E", true, v.E == nil)
t.Run("struct.field null", func(t *testing.T) { t.Run("struct.field null", func(t *testing.T) {
var v struct { var v struct {
A string A string
@ -179,8 +182,9 @@ func Test_Decoder(t *testing.T) {
D map[string]interface{} D map[string]interface{}
E [2]string E [2]string
F interface{} F interface{}
G func()
} }
assertErr(t, json.Unmarshal([]byte(`{"a":null,"b":null,"c":null,"d":null,"e":null,"f":null}`), &v)) assertErr(t, json.Unmarshal([]byte(`{"a":null,"b":null,"c":null,"d":null,"e":null,"f":null,"g":null}`), &v))
assertEq(t, "string", v.A, "") assertEq(t, "string", v.A, "")
assertNeq(t, "[]string", v.B, nil) assertNeq(t, "[]string", v.B, nil)
assertEq(t, "[]string", len(v.B), 0) assertEq(t, "[]string", len(v.B), 0)
@ -191,6 +195,7 @@ func Test_Decoder(t *testing.T) {
assertNeq(t, "array", v.E, nil) assertNeq(t, "array", v.E, nil)
assertEq(t, "array", len(v.E), 2) assertEq(t, "array", len(v.E), 2)
assertEq(t, "interface{}", v.F, nil) assertEq(t, "interface{}", v.F, nil)
assertEq(t, "nilfunc", true, v.G == nil)
}) })
}) })
t.Run("interface", func(t *testing.T) { t.Run("interface", func(t *testing.T) {
@ -239,6 +244,11 @@ func Test_Decoder(t *testing.T) {
assertEq(t, "interface", nil, v) assertEq(t, "interface", nil, v)
}) })
}) })
t.Run("func", func(t *testing.T) {
var v func()
assertErr(t, json.Unmarshal([]byte(`null`), &v))
assertEq(t, "nilfunc", true, v == nil)
})
} }
func TestIssue98(t *testing.T) { func TestIssue98(t *testing.T) {

View File

@ -123,11 +123,15 @@ func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecode
return compileFloat32(structName, fieldName) return compileFloat32(structName, fieldName)
case reflect.Float64: case reflect.Float64:
return compileFloat64(structName, fieldName) return compileFloat64(structName, fieldName)
case reflect.Func:
return compileFunc(typ, structName, fieldName)
} }
return nil, &errors.UnmarshalTypeError{ return nil, &errors.UnmarshalTypeError{
Value: "object", Value: "object",
Type: runtime.RType2Type(typ), Type: runtime.RType2Type(typ),
Offset: 0, Offset: 0,
Struct: structName,
Field: fieldName,
} }
} }
@ -178,6 +182,8 @@ ERROR:
Value: "object", Value: "object",
Type: runtime.RType2Type(typ), Type: runtime.RType2Type(typ),
Offset: 0, Offset: 0,
Struct: structName,
Field: fieldName,
} }
} }
@ -312,6 +318,10 @@ func compileInterface(typ *runtime.Type, structName, fieldName string) (Decoder,
return newInterfaceDecoder(typ, structName, fieldName), nil return newInterfaceDecoder(typ, structName, fieldName), nil
} }
func compileFunc(typ *runtime.Type, strutName, fieldName string) (Decoder, error) {
return newFuncDecoder(typ, strutName, fieldName), nil
}
func removeConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) { func removeConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) {
for k, v := range dec.fieldMap { for k, v := range dec.fieldMap {
if _, exists := conflictedMap[k]; exists { if _, exists := conflictedMap[k]; exists {

141
internal/decoder/func.go Normal file
View File

@ -0,0 +1,141 @@
package decoder
import (
"bytes"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type funcDecoder struct {
typ *runtime.Type
structName string
fieldName string
}
func newFuncDecoder(typ *runtime.Type, structName, fieldName string) *funcDecoder {
fnDecoder := &funcDecoder{typ, structName, fieldName}
return fnDecoder
}
func (d *funcDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
s.skipWhiteSpace()
start := s.cursor
if err := s.skipValue(depth); err != nil {
return err
}
src := s.buf[start:s.cursor]
if len(src) > 0 {
switch src[0] {
case '"':
return &errors.UnmarshalTypeError{
Value: "string",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case '[':
return &errors.UnmarshalTypeError{
Value: "array",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case '{':
return &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return &errors.UnmarshalTypeError{
Value: "number",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case 'n':
if err := nullBytes(s); err != nil {
return err
}
*(*unsafe.Pointer)(p) = nil
return nil
case 't':
if err := trueBytes(s); err == nil {
return &errors.UnmarshalTypeError{
Value: "boolean",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
}
case 'f':
if err := falseBytes(s); err == nil {
return &errors.UnmarshalTypeError{
Value: "boolean",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
}
}
}
return errors.ErrNotAtBeginningOfValue(start)
}
func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
buf := ctx.Buf
cursor = skipWhiteSpace(buf, cursor)
start := cursor
end, err := skipValue(buf, cursor, depth)
if err != nil {
return 0, err
}
src := buf[start:end]
if len(src) > 0 {
switch src[0] {
case '"':
return 0, &errors.UnmarshalTypeError{
Value: "string",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '[':
return 0, &errors.UnmarshalTypeError{
Value: "array",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '{':
return 0, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return 0, &errors.UnmarshalTypeError{
Value: "number",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case 'n':
if bytes.Equal(src, nullbytes) {
*(*unsafe.Pointer)(p) = nil
return end, nil
}
case 't':
if err := validateTrue(buf, start); err == nil {
return 0, &errors.UnmarshalTypeError{
Value: "boolean",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
}
case 'f':
if err := validateFalse(buf, start); err == nil {
return 0, &errors.UnmarshalTypeError{
Value: "boolean",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
}
}
}
return 0, errors.ErrNotAtBeginningOfValue(start)
}