mirror of https://github.com/goccy/go-json.git
Add error type compatible with encoding/json
This commit is contained in:
parent
a979b83d27
commit
d8d711ecf9
34
decode.go
34
decode.go
|
@ -73,16 +73,26 @@ func (d *Decoder) Buffered() io.Reader {
|
|||
return d.buffered()
|
||||
}
|
||||
|
||||
func (d *Decoder) validateType(typ *rtype, p uintptr) error {
|
||||
if typ.Kind() != reflect.Ptr || p == 0 {
|
||||
return &InvalidUnmarshalError{Type: rtype2type(typ)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decode(src []byte, header *interfaceHeader) error {
|
||||
typ := header.typ
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return ErrDecodePointer
|
||||
}
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := (*rtype)(unsafe.Pointer(typeptr))
|
||||
ptr := uintptr(header.ptr)
|
||||
|
||||
if err := d.validateType(copiedType, ptr); err != nil {
|
||||
return err
|
||||
}
|
||||
dec := cachedDecoder.get(typeptr)
|
||||
if dec == nil {
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := (*rtype)(unsafe.Pointer(typeptr))
|
||||
|
||||
compiledDec, err := d.compileHead(copiedType)
|
||||
if err != nil {
|
||||
|
@ -91,7 +101,6 @@ func (d *Decoder) decode(src []byte, header *interfaceHeader) error {
|
|||
cachedDecoder.set(typeptr, compiledDec)
|
||||
dec = compiledDec
|
||||
}
|
||||
ptr := uintptr(header.ptr)
|
||||
if _, err := dec.decode(src, 0, ptr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -117,10 +126,15 @@ func (d *Decoder) decodeForUnmarshalNoEscape(src []byte, v interface{}) error {
|
|||
func (d *Decoder) Decode(v interface{}) error {
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return ErrDecodePointer
|
||||
}
|
||||
ptr := uintptr(header.ptr)
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := (*rtype)(unsafe.Pointer(typeptr))
|
||||
|
||||
if err := d.validateType(copiedType, ptr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dec := cachedDecoder.get(typeptr)
|
||||
if dec == nil {
|
||||
compiledDec, err := d.compileHead(typ)
|
||||
|
@ -130,7 +144,7 @@ func (d *Decoder) Decode(v interface{}) error {
|
|||
cachedDecoder.set(typeptr, compiledDec)
|
||||
dec = compiledDec
|
||||
}
|
||||
ptr := uintptr(header.ptr)
|
||||
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := d.r.Read(buf)
|
||||
|
|
|
@ -218,3 +218,16 @@ func Test_UnmarshalText(t *testing.T) {
|
|||
assertEq(t, "unmarshal", v.v, 11)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_InvalidUnmarshalError(t *testing.T) {
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
var v *struct{}
|
||||
err := fmt.Sprint(json.Unmarshal([]byte(`{}`), v))
|
||||
assertEq(t, "invalid unmarshal error", "json: Unmarshal(nil *struct {})", err)
|
||||
})
|
||||
t.Run("non pointer", func(t *testing.T) {
|
||||
var v int
|
||||
err := fmt.Sprint(json.Unmarshal([]byte(`{}`), v))
|
||||
assertEq(t, "invalid unmarshal error", "json: Unmarshal(non-pointer int)", err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
|
||||
|
@ -71,7 +69,7 @@ func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) {
|
|||
case reflect.Interface:
|
||||
return e.compileInterface(typ)
|
||||
}
|
||||
return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType)
|
||||
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
||||
}
|
||||
|
||||
func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
|
||||
|
|
71
encode_vm.go
71
encode_vm.go
|
@ -2,7 +2,9 @@ package json
|
|||
|
||||
import (
|
||||
"encoding"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -47,7 +49,14 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeFloat32(e.ptrToFloat32(code.ptr))
|
||||
code = code.next
|
||||
case opFloat64:
|
||||
e.encodeFloat64(e.ptrToFloat64(code.ptr))
|
||||
v := e.ptrToFloat64(code.ptr)
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeFloat64(v)
|
||||
code = code.next
|
||||
case opString:
|
||||
e.encodeString(e.ptrToString(code.ptr))
|
||||
|
@ -519,9 +528,16 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeNull()
|
||||
code = field.end
|
||||
} else {
|
||||
v := e.ptrToFloat64(field.ptr + field.offset)
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeByte('{')
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeFloat64(e.ptrToFloat64(field.ptr + field.offset))
|
||||
e.encodeFloat64(v)
|
||||
field.nextField.ptr = field.ptr
|
||||
code = field.next
|
||||
}
|
||||
|
@ -809,12 +825,19 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeNull()
|
||||
code = field.end
|
||||
} else {
|
||||
v := e.ptrToFloat64(field.ptr + field.offset)
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeIndent(code.indent)
|
||||
e.encodeBytes([]byte{'{', '\n'})
|
||||
e.encodeIndent(code.indent + 1)
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeByte(' ')
|
||||
e.encodeFloat64(e.ptrToFloat64(field.ptr + field.offset))
|
||||
e.encodeFloat64(v)
|
||||
field.nextField.ptr = field.ptr
|
||||
code = field.next
|
||||
}
|
||||
|
@ -1176,6 +1199,12 @@ func (e *Encoder) run(code *opcode) error {
|
|||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeIndent(code.indent + 1)
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeFloat64(v)
|
||||
|
@ -1577,6 +1606,12 @@ func (e *Encoder) run(code *opcode) error {
|
|||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeIndent(code.indent + 1)
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeByte(' ')
|
||||
|
@ -1728,7 +1763,14 @@ func (e *Encoder) run(code *opcode) error {
|
|||
c := code.toStructFieldCode()
|
||||
c.nextField.ptr = c.ptr
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeFloat64(e.ptrToFloat64(c.ptr + c.offset))
|
||||
v := e.ptrToFloat64(c.ptr + c.offset)
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeFloat64(v)
|
||||
code = code.next
|
||||
case opStructFieldString:
|
||||
e.encodeByte(',')
|
||||
|
@ -1859,7 +1901,14 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeIndent(c.indent)
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeByte(' ')
|
||||
e.encodeFloat64(e.ptrToFloat64(c.ptr + c.offset))
|
||||
v := e.ptrToFloat64(c.ptr + c.offset)
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
e.encodeFloat64(v)
|
||||
code = code.next
|
||||
c.nextField.ptr = c.ptr
|
||||
case opStructFieldStringIndent:
|
||||
|
@ -2030,6 +2079,12 @@ func (e *Encoder) run(code *opcode) error {
|
|||
c := code.toStructFieldCode()
|
||||
v := e.ptrToFloat64(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
|
@ -2237,6 +2292,12 @@ func (e *Encoder) run(code *opcode) error {
|
|||
c := code.toStructFieldCode()
|
||||
v := e.ptrToFloat64(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if math.IsInf(v, 0) || math.IsNaN(v) {
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
if e.buf[len(e.buf)-2] != '{' {
|
||||
e.encodeBytes([]byte{',', '\n'})
|
||||
}
|
||||
|
|
120
error.go
120
error.go
|
@ -1,8 +1,118 @@
|
|||
package json
|
||||
|
||||
import "golang.org/x/xerrors"
|
||||
|
||||
var (
|
||||
ErrUnsupportedType = xerrors.New("json: unsupported type")
|
||||
ErrDecodePointer = xerrors.New("json: required pointer type")
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when
|
||||
// attempting to encode a string value with invalid UTF-8 sequences.
|
||||
// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by
|
||||
// replacing invalid bytes with the Unicode replacement rune U+FFFD.
|
||||
//
|
||||
// Deprecated: No longer used; kept for compatibility.
|
||||
type InvalidUTF8Error struct {
|
||||
S string // the whole string value that caused the error
|
||||
}
|
||||
|
||||
func (e *InvalidUTF8Error) Error() string {
|
||||
return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S))
|
||||
}
|
||||
|
||||
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
|
||||
// (The argument to Unmarshal must be a non-nil pointer.)
|
||||
type InvalidUnmarshalError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidUnmarshalError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "json: Unmarshal(nil)"
|
||||
}
|
||||
|
||||
if e.Type.Kind() != reflect.Ptr {
|
||||
return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type)
|
||||
}
|
||||
return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type)
|
||||
}
|
||||
|
||||
// A MarshalerError represents an error from calling a MarshalJSON or MarshalText method.
|
||||
type MarshalerError struct {
|
||||
Type reflect.Type
|
||||
Err error
|
||||
sourceFunc string
|
||||
}
|
||||
|
||||
func (e *MarshalerError) Error() string {
|
||||
srcFunc := e.sourceFunc
|
||||
if srcFunc == "" {
|
||||
srcFunc = "MarshalJSON"
|
||||
}
|
||||
return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error())
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error.
|
||||
func (e *MarshalerError) Unwrap() error { return e.Err }
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error.
|
||||
type SyntaxError struct {
|
||||
msg string // description of error
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string { return e.msg }
|
||||
|
||||
// An UnmarshalFieldError describes a JSON object key that
|
||||
// led to an unexported (and therefore unwritable) struct field.
|
||||
//
|
||||
// Deprecated: No longer used; kept for compatibility.
|
||||
type UnmarshalFieldError struct {
|
||||
Key string
|
||||
Type reflect.Type
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
||||
func (e *UnmarshalFieldError) Error() string {
|
||||
return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s",
|
||||
strconv.Quote(e.Key), e.Field.Name, e.Type.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// An UnmarshalTypeError describes a JSON value that was
|
||||
// not appropriate for a value of a specific Go type.
|
||||
type UnmarshalTypeError struct {
|
||||
Value string // description of JSON value - "bool", "array", "number -5"
|
||||
Type reflect.Type // type of Go value it could not be assigned to
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
Struct string // name of the struct type containing the field
|
||||
Field string // the full path from root node to the field
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Error() string {
|
||||
if e.Struct != "" || e.Field != "" {
|
||||
return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s",
|
||||
e.Value, e.Struct, e.Field, e.Type,
|
||||
)
|
||||
}
|
||||
return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type)
|
||||
}
|
||||
|
||||
// An UnsupportedTypeError is returned by Marshal when attempting
|
||||
// to encode an unsupported value type.
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *UnsupportedTypeError) Error() string {
|
||||
return fmt.Sprintf("json: unsupported type: %s", e.Type)
|
||||
}
|
||||
|
||||
type UnsupportedValueError struct {
|
||||
Value reflect.Value
|
||||
Str string
|
||||
}
|
||||
|
||||
func (e *UnsupportedValueError) Error() string {
|
||||
return fmt.Sprintf("json: unsupported value: %s", e.Str)
|
||||
}
|
||||
|
|
74
json.go
74
json.go
|
@ -177,6 +177,80 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
|||
return bytes, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the JSON-encoded data and stores the result
|
||||
// in the value pointed to by v. If v is nil or not a pointer,
|
||||
// Unmarshal returns an InvalidUnmarshalError.
|
||||
//
|
||||
// Unmarshal uses the inverse of the encodings that
|
||||
// Marshal uses, allocating maps, slices, and pointers as necessary,
|
||||
// with the following additional rules:
|
||||
//
|
||||
// To unmarshal JSON into a pointer, Unmarshal first handles the case of
|
||||
// the JSON being the JSON literal null. In that case, Unmarshal sets
|
||||
// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
|
||||
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
|
||||
// allocates a new value for it to point to.
|
||||
//
|
||||
// To unmarshal JSON into a value implementing the Unmarshaler interface,
|
||||
// Unmarshal calls that value's UnmarshalJSON method, including
|
||||
// when the input is a JSON null.
|
||||
// Otherwise, if the value implements encoding.TextUnmarshaler
|
||||
// and the input is a JSON quoted string, Unmarshal calls that value's
|
||||
// UnmarshalText method with the unquoted form of the string.
|
||||
//
|
||||
// To unmarshal JSON into a struct, Unmarshal matches incoming object
|
||||
// keys to the keys used by Marshal (either the struct field name or its tag),
|
||||
// preferring an exact match but also accepting a case-insensitive match. By
|
||||
// default, object keys which don't have a corresponding struct field are
|
||||
// ignored (see Decoder.DisallowUnknownFields for an alternative).
|
||||
//
|
||||
// To unmarshal JSON into an interface value,
|
||||
// Unmarshal stores one of these in the interface value:
|
||||
//
|
||||
// bool, for JSON booleans
|
||||
// float64, for JSON numbers
|
||||
// string, for JSON strings
|
||||
// []interface{}, for JSON arrays
|
||||
// map[string]interface{}, for JSON objects
|
||||
// nil for JSON null
|
||||
//
|
||||
// To unmarshal a JSON array into a slice, Unmarshal resets the slice length
|
||||
// to zero and then appends each element to the slice.
|
||||
// As a special case, to unmarshal an empty JSON array into a slice,
|
||||
// Unmarshal replaces the slice with a new empty slice.
|
||||
//
|
||||
// To unmarshal a JSON array into a Go array, Unmarshal decodes
|
||||
// JSON array elements into corresponding Go array elements.
|
||||
// If the Go array is smaller than the JSON array,
|
||||
// the additional JSON array elements are discarded.
|
||||
// If the JSON array is smaller than the Go array,
|
||||
// the additional Go array elements are set to zero values.
|
||||
//
|
||||
// To unmarshal a JSON object into a map, Unmarshal first establishes a map to
|
||||
// use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal
|
||||
// reuses the existing map, keeping existing entries. Unmarshal then stores
|
||||
// key-value pairs from the JSON object into the map. The map's key type must
|
||||
// either be any string type, an integer, implement json.Unmarshaler, or
|
||||
// implement encoding.TextUnmarshaler.
|
||||
//
|
||||
// If a JSON value is not appropriate for a given target type,
|
||||
// or if a JSON number overflows the target type, Unmarshal
|
||||
// skips that field and completes the unmarshaling as best it can.
|
||||
// If no more serious errors are encountered, Unmarshal returns
|
||||
// an UnmarshalTypeError describing the earliest such error. In any
|
||||
// case, it's not guaranteed that all the remaining fields following
|
||||
// the problematic one will be unmarshaled into the target object.
|
||||
//
|
||||
// The JSON null value unmarshals into an interface, map, pointer, or slice
|
||||
// by setting that Go value to nil. Because null is often used in JSON to mean
|
||||
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
|
||||
// on the value and produces no error.
|
||||
//
|
||||
// When unmarshaling quoted strings, invalid UTF-8 or
|
||||
// invalid UTF-16 surrogate pairs are not treated as an error.
|
||||
// Instead, they are replaced by the Unicode replacement
|
||||
// character U+FFFD.
|
||||
//
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
src := make([]byte, len(data)+1) // append nul byte to end
|
||||
copy(src, data)
|
||||
|
|
Loading…
Reference in New Issue