Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+271
@@ -0,0 +1,271 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !goexperiment.jsonv2 || !go1.25
|
||||
|
||||
// Represents JSON data structure using native Go types: booleans, floats,
|
||||
// strings, arrays, and maps.
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
jsonv2 "github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/internal/jsonwire"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The JSON input is decoded according the following rules:
|
||||
//
|
||||
// - If the value type implements [jsonv2.UnmarshalerFrom],
|
||||
// then the UnmarshalJSONFrom method is called to decode the JSON value.
|
||||
// If the method returns [errors.ErrUnsupported],
|
||||
// then the input is decoded according to subsequent rules.
|
||||
//
|
||||
// - If the value type implements [Unmarshaler],
|
||||
// then the UnmarshalJSON method is called to decode the JSON value,
|
||||
// including when the input is a JSON null.
|
||||
//
|
||||
// - If the value implements [encoding.TextUnmarshaler] and
|
||||
// the input is a JSON string, then the UnmarshalText method
|
||||
// is called with the unquoted form of the string.
|
||||
//
|
||||
// Otherwise, Unmarshal uses the following type-dependent default decodings:
|
||||
//
|
||||
// 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
|
||||
// - []any, for JSON arrays
|
||||
// - map[string]any, for JSON objects
|
||||
// - nil for JSON null
|
||||
//
|
||||
// To unmarshal a JSON array into a slice, Unmarshal decodes each JSON array
|
||||
// element into the corresponding slice element, reusing existing slice
|
||||
// elements in-place. The slice grows to accommodate additional elements,
|
||||
// or is truncated if the JSON array is shorter.
|
||||
// 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, or implement [encoding.TextUnmarshaler].
|
||||
//
|
||||
// If the JSON-encoded data contain a syntax error, Unmarshal returns a [SyntaxError].
|
||||
//
|
||||
// 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 any) error {
|
||||
return jsonv2.Unmarshal(data, v, DefaultOptionsV1())
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface implemented by types
|
||||
// that can unmarshal a JSON description of themselves.
|
||||
// The input can be assumed to be a valid encoding of
|
||||
// a JSON value. UnmarshalJSON must copy the JSON data
|
||||
// if it wishes to retain the data after returning.
|
||||
type Unmarshaler = jsonv2.Unmarshaler
|
||||
|
||||
// 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 root type containing the field
|
||||
Field string // the full path from root node to the value
|
||||
Err error // may be nil
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Error() string {
|
||||
var s string
|
||||
if e.Struct != "" || e.Field != "" {
|
||||
// The design of UnmarshalTypeError overly assumes a struct-based
|
||||
// Go representation for the JSON value.
|
||||
// The logic in jsontext represents paths using a JSON Pointer,
|
||||
// which is agnostic to the Go type system.
|
||||
// Trying to convert a JSON Pointer into an UnmarshalTypeError.Field
|
||||
// is difficult. As a heuristic, if the last path token looks like
|
||||
// an index into a JSON array (e.g., ".foo.bar.0"),
|
||||
// avoid the phrase "Go struct field ".
|
||||
intoWhat := "Go struct field "
|
||||
i := strings.LastIndexByte(e.Field, '.') + len(".")
|
||||
if len(e.Field[i:]) > 0 && strings.TrimRight(e.Field[i:], "0123456789") == "" {
|
||||
intoWhat = "" // likely a Go slice or array
|
||||
}
|
||||
s = "json: cannot unmarshal " + e.Value + " into " + intoWhat + e.Struct + "." + e.Field + " of type " + e.Type.String()
|
||||
} else {
|
||||
s = "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
|
||||
}
|
||||
if e.Err != nil {
|
||||
s += ": " + e.Err.Error()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// 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 "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String()
|
||||
}
|
||||
|
||||
// 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.Pointer {
|
||||
return "json: Unmarshal(non-pointer " + e.Type.String() + ")"
|
||||
}
|
||||
return "json: Unmarshal(nil " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
// A Number represents a JSON number literal.
|
||||
type Number string
|
||||
|
||||
// String returns the literal text of the number.
|
||||
func (n Number) String() string { return string(n) }
|
||||
|
||||
// Float64 returns the number as a float64.
|
||||
func (n Number) Float64() (float64, error) {
|
||||
return strconv.ParseFloat(string(n), 64)
|
||||
}
|
||||
|
||||
// Int64 returns the number as an int64.
|
||||
func (n Number) Int64() (int64, error) {
|
||||
return strconv.ParseInt(string(n), 10, 64)
|
||||
}
|
||||
|
||||
var numberType = reflect.TypeFor[Number]()
|
||||
|
||||
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
||||
func (n Number) MarshalJSONTo(enc *jsontext.Encoder) error {
|
||||
opts := enc.Options()
|
||||
stringify, _ := jsonv2.GetOption(opts, jsonv2.StringifyNumbers)
|
||||
if k, n := enc.StackIndex(enc.StackDepth()); k == '{' && n%2 == 0 {
|
||||
stringify = true // expecting a JSON object name
|
||||
}
|
||||
n = cmp.Or(n, "0")
|
||||
var num []byte
|
||||
val := enc.AvailableBuffer()
|
||||
if stringify {
|
||||
val = append(val, '"')
|
||||
val = append(val, n...)
|
||||
val = append(val, '"')
|
||||
num = val[len(`"`) : len(val)-len(`"`)]
|
||||
} else {
|
||||
val = append(val, n...)
|
||||
num = val
|
||||
}
|
||||
if n, err := jsonwire.ConsumeNumber(num); n != len(num) || err != nil {
|
||||
return fmt.Errorf("cannot parse %q as JSON number: %w", val, strconv.ErrSyntax)
|
||||
}
|
||||
return enc.WriteValue(val)
|
||||
}
|
||||
|
||||
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
||||
func (n *Number) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
||||
opts := dec.Options()
|
||||
stringify, _ := jsonv2.GetOption(opts, jsonv2.StringifyNumbers)
|
||||
if k, n := dec.StackIndex(dec.StackDepth()); k == '{' && n%2 == 0 {
|
||||
stringify = true // expecting a JSON object name
|
||||
}
|
||||
val, err := dec.ReadValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val0 := val
|
||||
k := val.Kind()
|
||||
switch k {
|
||||
case 'n':
|
||||
if legacy, _ := jsonv2.GetOption(opts, MergeWithLegacySemantics); !legacy {
|
||||
*n = ""
|
||||
}
|
||||
return nil
|
||||
case '"':
|
||||
verbatim := jsonwire.ConsumeSimpleString(val) == len(val)
|
||||
val = jsonwire.UnquoteMayCopy(val, verbatim)
|
||||
if n, err := jsonwire.ConsumeNumber(val); n != len(val) || err != nil {
|
||||
return &jsonv2.SemanticError{JSONKind: val0.Kind(), JSONValue: val0.Clone(), GoType: numberType, Err: strconv.ErrSyntax}
|
||||
}
|
||||
*n = Number(val)
|
||||
return nil
|
||||
case '0':
|
||||
if stringify {
|
||||
break
|
||||
}
|
||||
*n = Number(val)
|
||||
return nil
|
||||
}
|
||||
return &jsonv2.SemanticError{JSONKind: k, GoType: numberType}
|
||||
}
|
||||
Reference in new issue
Block a user