This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+875
View File
@@ -0,0 +1,875 @@
// Copyright 2025 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.
// Code generated by alias_gen.go; DO NOT EDIT.
//go:build goexperiment.jsonv2 && go1.25
// Package json implements encoding and decoding of JSON as defined in
// RFC 7159. The mapping between JSON and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
//
// See [JSON and Go] for an introduction to this package.
//
// # Security Considerations
//
// See the "Security Considerations" section in [encoding/json/v2].
//
// For historical reasons, the default behavior of v1 [encoding/json]
// unfortunately operates with less secure defaults.
// New usages of JSON in Go are encouraged to use [encoding/json/v2] instead.
//
// [JSON and Go]: https://go.dev/blog/json
// Migrating to v2
//
// This package (i.e., [encoding/json]) is now formally known as the v1 package
// since a v2 package now exists at [encoding/json/v2].
// All the behavior of the v1 package is implemented in terms of
// the v2 package with the appropriate set of options specified that
// preserve the historical behavior of v1.
//
// The [jsonv2.Marshal] function is the newer equivalent of v1 [Marshal].
// The [jsonv2.Unmarshal] function is the newer equivalent of v1 [Unmarshal].
// The v2 functions have the same calling signature as the v1 equivalent
// except that they take in variadic [Options] arguments that can be specified
// to alter the behavior of marshal or unmarshal. Both v1 and v2 generally
// behave in similar ways, but there are some notable differences.
//
// The following is a list of differences between v1 and v2:
//
// - In v1, JSON object members are unmarshaled into a Go struct using a
// case-insensitive name match with the JSON name of the fields.
// In contrast, v2 matches fields using an exact, case-sensitive match.
// The [jsonv2.MatchCaseInsensitiveNames] and [MatchCaseSensitiveDelimiter]
// options control this behavior difference. To explicitly specify a Go struct
// field to use a particular name matching scheme, either the `case:ignore`
// or the `case:strict` field option can be specified.
// Field-specified options take precedence over caller-specified options.
//
// - In v1, when marshaling a Go struct, a field marked as `omitempty`
// is omitted if the field value is an "empty" Go value, which is defined as
// false, 0, a nil pointer, a nil interface value, and
// any empty array, slice, map, or string. In contrast, v2 redefines
// `omitempty` to omit a field if it encodes as an "empty" JSON value,
// which is defined as a JSON null, or an empty JSON string, object, or array.
// The [OmitEmptyWithLegacySemantics] option controls this behavior difference.
// Note that `omitempty` behaves identically in both v1 and v2 for a
// Go array, slice, map, or string (assuming no user-defined MarshalJSON method
// overrides the default representation). Existing usages of `omitempty` on a
// Go bool, number, pointer, or interface value should migrate to specifying
// `omitzero` instead (which is identically supported in both v1 and v2).
//
// - In v1, a Go struct field marked as `string` can be used to quote a
// Go string, bool, number, or pointer to such as a JSON string.
// In contrast, v2 restricts the `string` option to only quote a value
// that would normally be represented as a JSON number,
// but also expands support for it to operate with any Go type
// that would normally be represented as a JSON number.
// The [StringifyWithLegacySemantics] option controls this behavior difference.
//
// - In v1, a nil Go slice or Go map is marshaled as a JSON null.
// In contrast, v2 marshals a nil Go slice or Go map as
// an empty JSON array or JSON object, respectively.
// The [jsonv2.FormatNilSliceAsNull] and [jsonv2.FormatNilMapAsNull] options
// control this behavior difference.
//
// - In v1, a Go array may be unmarshaled from a JSON array of any length.
// In contrast, in v2 a Go array must be unmarshaled from a JSON array
// of the same length, otherwise it results in an error.
// The [UnmarshalArrayFromAnyLength] option controls this behavior difference.
//
// - In v1, a Go byte array is represented as a JSON array of JSON numbers.
// In contrast, in v2 a Go byte array is represented as a Base64-encoded JSON string.
// The [FormatByteArrayAsArray] option controls this behavior difference.
//
// - In v1, MarshalJSON methods declared on a pointer receiver are only called
// if the Go value is addressable. In contrast, in v2 a MarshalJSON method
// is always callable regardless of addressability.
// The [CallMethodsWithLegacySemantics] option controls this behavior difference.
//
// - In v1, MarshalJSON and UnmarshalJSON methods are never called for Go map keys.
// In contrast, in v2 a MarshalJSON or UnmarshalJSON method is eligible for
// being called for Go map keys.
// The [CallMethodsWithLegacySemantics] option controls this behavior difference.
//
// - In v1, a Go map is marshaled in a deterministic order.
// In contrast, in v2 a Go map is marshaled in a non-deterministic order.
// The [jsonv2.Deterministic] option controls this behavior difference.
//
// - In v1, JSON strings are encoded with HTML-specific or JavaScript-specific
// characters being escaped. In contrast, in v2 JSON strings use the minimal
// encoding and only escape if required by the JSON grammar.
// The [jsontext.EscapeForHTML] and [jsontext.EscapeForJS] options
// control this behavior difference.
//
// - In v1, bytes of invalid UTF-8 within a string are silently replaced with
// the Unicode replacement character. In contrast, in v2 the presence of
// invalid UTF-8 results in an error. The [jsontext.AllowInvalidUTF8] option
// controls this behavior difference.
//
// - In v1, a JSON object with duplicate names is permitted.
// In contrast, in v2 a JSON object with duplicate names results in an error.
// The [jsontext.AllowDuplicateNames] option controls this behavior difference.
//
// - In v1, when unmarshaling a JSON null into a non-empty Go value it will
// inconsistently either zero out the value or do nothing.
// In contrast, in v2 unmarshaling a JSON null will consistently and always
// zero out the underlying Go value. The [MergeWithLegacySemantics] option
// controls this behavior difference.
//
// - In v1, when unmarshaling a JSON value into a non-zero Go value,
// it merges into the original Go value for array elements, slice elements,
// struct fields (but not map values),
// pointer values, and interface values (only if a non-nil pointer).
// In contrast, in v2 unmarshal merges into the Go value
// for struct fields, map values, pointer values, and interface values.
// In general, the v2 semantic merges when unmarshaling a JSON object,
// otherwise it replaces the value. The [MergeWithLegacySemantics] option
// controls this behavior difference.
//
// - In v1, a [time.Duration] is represented as a JSON number containing
// the decimal number of nanoseconds. In contrast, in v2 a [time.Duration]
// has no default representation and results in a runtime error.
// The [FormatDurationAsNano] option controls this behavior difference.
//
// - In v1, errors are never reported at runtime for Go struct types
// that have some form of structural error (e.g., a malformed tag option).
// In contrast, v2 reports a runtime error for Go types that are invalid
// as they relate to JSON serialization. For example, a Go struct
// with only unexported fields cannot be serialized.
// The [ReportErrorsWithLegacySemantics] option controls this behavior difference.
//
// As mentioned, the entirety of v1 is implemented in terms of v2,
// where options are implicitly specified to opt into legacy behavior.
// For example, [Marshal] directly calls [jsonv2.Marshal] with [DefaultOptionsV1].
// Similarly, [Unmarshal] directly calls [jsonv2.Unmarshal] with [DefaultOptionsV1].
// The [DefaultOptionsV1] option represents the set of all options that specify
// default v1 behavior.
//
// For many of the behavior differences, there are Go struct field options
// that the author of a Go type can specify to control the behavior such that
// the type is represented identically in JSON under either v1 or v2 semantics.
//
// The availability of [DefaultOptionsV1] and [jsonv2.DefaultOptionsV2],
// where later options take precedence over former options allows for
// a gradual migration from v1 to v2. For example:
//
// - jsonv1.Marshal(v)
// uses default v1 semantics.
//
// - jsonv2.Marshal(v, jsonv1.DefaultOptionsV1())
// is semantically equivalent to jsonv1.Marshal
// and thus uses default v1 semantics.
//
// - jsonv2.Marshal(v, jsonv1.DefaultOptionsV1(), jsontext.AllowDuplicateNames(false))
// uses mostly v1 semantics, but opts into one particular v2-specific behavior.
//
// - jsonv2.Marshal(v, jsonv1.CallMethodsWithLegacySemantics(true))
// uses mostly v2 semantics, but opts into one particular v1-specific behavior.
//
// - jsonv2.Marshal(v, ..., jsonv2.DefaultOptionsV2())
// is semantically equivalent to jsonv2.Marshal since
// jsonv2.DefaultOptionsV2 overrides any options specified earlier
// and thus uses default v2 semantics.
//
// - jsonv2.Marshal(v)
// uses default v2 semantics.
//
// All new usages of "json" in Go should use the v2 package,
// but the v1 package will forever remain supported.
//
// See the [encoding/json/v2 Migration Guide] for additional detail on migration approaches.
//
// [encoding/json/v2 Migration Guide]: https://go.dev/doc/jsonv2-migration
package json
import (
"bytes"
"encoding/json"
"io"
)
// 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 json.Unmarshal(data, v)
}
// 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 = json.Unmarshaler
// An UnmarshalTypeError describes a JSON value that was
// not appropriate for a value of a specific Go type.
type UnmarshalTypeError = json.UnmarshalTypeError
// 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 = json.UnmarshalFieldError
// An InvalidUnmarshalError describes an invalid argument passed to [Unmarshal].
// (The argument to [Unmarshal] must be a non-nil pointer.)
type InvalidUnmarshalError = json.InvalidUnmarshalError
// A Number represents a JSON number literal.
type Number = json.Number
// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
//
// The input value is encoded as JSON according the following rules:
//
// - If the value type implements [jsonv2.MarshalerTo],
// then the MarshalJSONTo method is called to encode the value.
// If the method returns [errors.ErrUnsupported],
// then the input is encoded according to subsequent rules.
//
// - If the value type implements [Marshaler],
// then the MarshalJSON method is called to encode the value.
//
// - If the value type implements [encoding.TextAppender],
// then the AppendText method is called to encode the value and
// subsequently encode its result as a JSON string.
//
// - If the value type implements [encoding.TextMarshaler],
// then the MarshalText method is called to encode the value and
// subsequently encode its result as a JSON string.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as JSON booleans.
//
// Floating point, integer, and [Number] values encode as JSON numbers.
// NaN and +/-Inf values will return an [UnsupportedValueError].
//
// String values encode as JSON strings coerced to valid UTF-8,
// replacing invalid bytes with the Unicode replacement rune.
// So that the JSON will be safe to embed inside HTML <script> tags,
// the string is encoded using [HTMLEscape],
// which replaces "<", ">", "&", U+2028, and U+2029 are escaped
// to "\u003c","\u003e", "\u0026", "\u2028", and "\u2029".
// This replacement can be disabled when using an [Encoder],
// by calling [Encoder.SetEscapeHTML](false).
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string, and a nil slice
// encodes as the null JSON value.
//
// Struct values encode as JSON objects.
// Each exported struct field becomes a member of the object, using the
// field name as the object key, unless the field is omitted for one of the
// reasons given below.
//
// The encoding of each struct field can be customized by the format string
// stored under the "json" key in the struct field's tag.
// The format string gives the name of the field, possibly followed by a
// comma-separated list of options. The name may be empty in order to
// specify options without overriding the default field name.
//
// The "omitempty" option specifies that the field should be omitted
// from the encoding if the field has an empty value, defined as
// false, 0, a nil pointer, a nil interface value, and any array,
// slice, map, or string of length zero.
//
// As a special case, if the field tag is "-", the field is always omitted.
// JSON names containing commas or quotes, or names identical to "" or "-",
// can be specified using a single-quoted string literal, where the syntax
// is identical to the Go grammar for a double-quoted string literal,
// but instead uses single quotes as the delimiters.
//
// Examples of struct field tags and their meanings:
//
// // Field appears in JSON as key "myName".
// Field int `json:"myName"`
//
// // Field appears in JSON as key "myName" and
// // the field is omitted from the object if its value is empty,
// // as defined above.
// Field int `json:"myName,omitempty"`
//
// // Field appears in JSON as key "Field" (the default), but
// // the field is skipped if empty.
// // Note the leading comma.
// Field int `json:",omitempty"`
//
// // Field is ignored by this package.
// Field int `json:"-"`
//
// // Field appears in JSON as key "-".
// Field int `json:"'-'"`
//
// The "omitzero" option specifies that the field should be omitted
// from the encoding if the field has a zero value, according to rules:
//
// 1) If the field type has an "IsZero() bool" method, that will be used to
// determine whether the value is zero.
//
// 2) Otherwise, the value is zero if it is the zero value for its type.
//
// If both "omitempty" and "omitzero" are specified, the field will be omitted
// if the value is either empty or zero (or both).
//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
// when communicating with JavaScript programs:
//
// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, and ASCII punctuation except quotation
// marks, backslash, and comma.
//
// Embedded struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules amended
// as described in the next paragraph.
// An anonymous struct field with a name given in its JSON tag is treated as
// having that name, rather than being anonymous.
// An anonymous struct field of interface type is treated the same as having
// that type as its name, rather than being anonymous.
//
// The Go visibility rules for struct fields are amended for JSON when
// deciding which field to marshal or unmarshal. If there are
// multiple fields at the same level, and that level is the least
// nested (and would therefore be the nesting level selected by the
// usual Go rules), the following extra rules apply:
//
// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
// even if there are multiple untagged fields that would otherwise conflict.
//
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
//
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
//
// Handling of anonymous struct fields is new in Go 1.1.
// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of
// an anonymous struct field in both current and earlier versions, give the field
// a JSON tag of "-".
//
// Map values encode as JSON objects. The map's key type must either be a
// string, an integer type, or implement [encoding.TextMarshaler]. The map keys
// are sorted and used as JSON object keys by applying the following rules,
// subject to the UTF-8 coercion described for string values above:
// - keys of any string type are used directly
// - keys that implement [encoding.TextMarshaler] are marshaled
// - integer keys are converted to strings
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null JSON value.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON value.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an [UnsupportedTypeError].
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an error.
func Marshal(v any) ([]byte, error) {
return json.Marshal(v)
}
// MarshalIndent is like [Marshal] but applies [Indent] to format the output.
// Each JSON element in the output will begin on a new line beginning with prefix
// followed by one or more copies of indent according to the indentation nesting.
func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
return json.MarshalIndent(v, prefix, indent)
}
// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler = json.Marshaler
// An UnsupportedTypeError is returned by [Marshal] when attempting
// to encode an unsupported value type.
type UnsupportedTypeError = json.UnsupportedTypeError
// An UnsupportedValueError is returned by [Marshal] when attempting
// to encode an unsupported value.
type UnsupportedValueError = json.UnsupportedValueError
// 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 = json.InvalidUTF8Error
// A MarshalerError represents an error from calling a
// [Marshaler.MarshalJSON] or [encoding.TextMarshaler.MarshalText] method.
type MarshalerError = json.MarshalerError
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
// so that the JSON will be safe to embed inside HTML <script> tags.
// For historical reasons, web browsers don't honor standard HTML
// escaping within <script> tags, so an alternative JSON encoding must be used.
func HTMLEscape(dst *bytes.Buffer, src []byte) {
json.HTMLEscape(dst, src)
}
// Compact appends to dst the JSON-encoded src with
// insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error {
return json.Compact(dst, src)
}
// Indent appends to dst an indented form of the JSON-encoded src.
// Each element in a JSON object or array begins on a new,
// indented line beginning with prefix followed by one or more
// copies of indent according to the indentation nesting.
// The data appended to dst does not begin with the prefix nor
// any indentation, to make it easier to embed inside other formatted JSON data.
// Although leading space characters (space, tab, carriage return, newline)
// at the beginning of src are dropped, trailing space characters
// at the end of src are preserved and copied to dst.
// For example, if src has no trailing spaces, neither will dst;
// if src ends in a trailing newline, so will dst.
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return json.Indent(dst, src, prefix, indent)
}
// Options are a set of options to configure the v2 "json" package
// to operate with v1 semantics for particular features.
// Values of this type can be passed to v2 functions like
// [jsonv2.Marshal] or [jsonv2.Unmarshal].
// Instead of referencing this type, use [jsonv2.Options].
//
// See the "Migrating to v2" section for guidance on how to migrate usage
// of "json" from using v1 to using v2 instead.
type Options = json.Options
// DefaultOptionsV1 is the full set of all options that define v1 semantics.
// It is equivalent to the following boolean options being set to true:
//
// - [CallMethodsWithLegacySemantics]
// - [FormatByteArrayAsArray]
// - [FormatBytesWithLegacySemantics]
// - [FormatDurationAsNano]
// - [MatchCaseSensitiveDelimiter]
// - [MergeWithLegacySemantics]
// - [OmitEmptyWithLegacySemantics]
// - [ParseBytesWithLooseRFC4648]
// - [ParseTimeWithLooseRFC3339]
// - [ReportErrorsWithLegacySemantics]
// - [StringifyWithLegacySemantics]
// - [UnmarshalArrayFromAnyLength]
// - [jsonv2.Deterministic]
// - [jsonv2.FormatNilMapAsNull]
// - [jsonv2.FormatNilSliceAsNull]
// - [jsonv2.MatchCaseInsensitiveNames]
// - [jsontext.AllowDuplicateNames]
// - [jsontext.AllowInvalidUTF8]
// - [jsontext.EscapeForHTML]
// - [jsontext.EscapeForJS]
// - [jsontext.PreserveRawStrings]
//
// All other options are not present.
//
// The [Marshal] and [Unmarshal] functions in this package are
// semantically identical to calling the v2 equivalents with this option:
//
// jsonv2.Marshal(v, jsonv1.DefaultOptionsV1())
// jsonv2.Unmarshal(b, v, jsonv1.DefaultOptionsV1())
func DefaultOptionsV1() Options {
return json.DefaultOptionsV1()
}
// CallMethodsWithLegacySemantics specifies that calling of type-provided
// marshal and unmarshal methods follow legacy semantics:
//
// - When marshaling, a marshal method declared on a pointer receiver
// is only called if the Go value is addressable.
// Values obtained from an interface or map element are not addressable.
// Values obtained from a pointer or slice element are addressable.
// Values obtained from an array element or struct field inherit
// the addressability of the parent. In contrast, the v2 semantic
// is to always call marshal methods regardless of addressability.
//
// - When marshaling or unmarshaling, the [Marshaler] or [Unmarshaler]
// methods are ignored for map keys. However, [encoding.TextMarshaler]
// or [encoding.TextUnmarshaler] are still callable.
// In contrast, the v2 semantic is to serialize map keys
// like any other value (with regard to calling methods),
// which may include calling [Marshaler] or [Unmarshaler] methods,
// where it is the implementation's responsibility to represent the
// Go value as a JSON string (as required for JSON object names).
//
// - When marshaling, if a map key value implements a marshal method
// and is a nil pointer, then it is serialized as an empty JSON string.
// In contrast, the v2 semantic is to report an error.
//
// - When marshaling, if an interface type implements a marshal method
// and the interface value is a nil pointer to a concrete type,
// then the marshal method is always called.
// In contrast, the v2 semantic is to never directly call methods
// on interface values and to instead defer evaluation based upon
// the underlying concrete value. Similar to non-interface values,
// marshal methods are not called on nil pointers and
// are instead serialized as a JSON null.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func CallMethodsWithLegacySemantics(v bool) Options {
return json.CallMethodsWithLegacySemantics(v)
}
// FormatByteArrayAsArray specifies that a Go [N]byte is
// formatted as a normal Go array in contrast to the v2 default of
// formatting [N]byte as using binary data encoding (RFC 4648).
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func FormatByteArrayAsArray(v bool) Options {
return json.FormatByteArrayAsArray(v)
}
// FormatBytesWithLegacySemantics specifies that handling of
// []~byte and [N]~byte types follow legacy semantics:
//
// - A Go []~byte is to be treated as using some form of
// binary data encoding (RFC 4648) in contrast to the v2 default
// of only treating []byte as such. In particular, v2 does not
// treat slices of named byte types as representing binary data.
//
// - When marshaling, if a named byte implements a marshal method,
// then the slice is serialized as a JSON array of elements,
// each of which call the marshal method.
//
// - When unmarshaling, if the input is a JSON array,
// then unmarshal into the []~byte as if it were a normal Go slice.
// In contrast, the v2 default is to report an error unmarshaling
// a JSON array when expecting some form of binary data encoding.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func FormatBytesWithLegacySemantics(v bool) Options {
return json.FormatBytesWithLegacySemantics(v)
}
// FormatDurationAsNano specifies that a [time.Duration] is
// formatted as a JSON number representing the number of nanoseconds
// in contrast to the v2 default of reporting an error.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func FormatDurationAsNano(v bool) Options {
return json.FormatDurationAsNano(v)
}
// MatchCaseSensitiveDelimiter specifies that underscores and dashes are
// not to be ignored when performing case-insensitive name matching which
// occurs under [jsonv2.MatchCaseInsensitiveNames] or the `case:ignore` tag option.
// Thus, case-insensitive name matching is identical to [strings.EqualFold].
// Use of this option diminishes the ability of case-insensitive matching
// to be able to match common case variants (e.g., "foo_bar" with "fooBar").
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func MatchCaseSensitiveDelimiter(v bool) Options {
return json.MatchCaseSensitiveDelimiter(v)
}
// MergeWithLegacySemantics specifies that unmarshaling into a non-zero
// Go value follows legacy semantics:
//
// - When unmarshaling a JSON null, this preserves the original Go value
// if the kind is a bool, int, uint, float, string, array, or struct.
// Otherwise, it zeros the Go value.
// In contrast, the default v2 behavior is to consistently and always
// zero the Go value when unmarshaling a JSON null into it.
//
// - When unmarshaling a JSON value other than null, this merges into
// the original Go value for array elements, slice elements,
// struct fields (but not map values),
// pointer values, and interface values (only if a non-nil pointer).
// For slices, it will merge into the pre-existing value of slice elements
// even for those past the slice length. If the original slice length
// was longer than the JSON array, then it is truncated to match.
// In contrast, the default v2 behavior is to merge into the Go value
// for struct fields, map values, pointer values, and interface values.
// In general, the v2 semantic merges when unmarshaling a JSON object,
// otherwise it replaces the original value.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func MergeWithLegacySemantics(v bool) Options {
return json.MergeWithLegacySemantics(v)
}
// OmitEmptyWithLegacySemantics specifies that the `omitempty` tag option
// follows a definition of empty where a field is omitted if the Go value is
// false, 0, a nil pointer, a nil interface value,
// or any empty array, slice, map, or string.
// This overrides the v2 semantic where a field is empty if the value
// marshals as a JSON null or an empty JSON string, object, or array.
//
// The v1 and v2 definitions of `omitempty` are practically the same for
// Go strings, slices, arrays, and maps. Usages of `omitempty` on
// Go bools, ints, uints, floats, pointers, and interfaces should migrate to use
// the `omitzero` tag option, which omits a field if it is the zero Go value.
//
// This only affects marshaling and is ignored when unmarshaling.
// The v1 default is true.
func OmitEmptyWithLegacySemantics(v bool) Options {
return json.OmitEmptyWithLegacySemantics(v)
}
// ParseBytesWithLooseRFC4648 specifies that when parsing
// binary data encoded as "base32" or "base64",
// to ignore the presence of '\r' and '\n' characters.
// In contrast, the v2 default is to report an error in order to be
// strictly compliant with RFC 4648, section 3.3,
// which specifies that non-alphabet characters must be rejected.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func ParseBytesWithLooseRFC4648(v bool) Options {
return json.ParseBytesWithLooseRFC4648(v)
}
// ParseTimeWithLooseRFC3339 specifies that a [time.Time]
// parses according to loose adherence to RFC 3339.
// In particular, it permits historically incorrect representations,
// allowing for deviations in hour format, sub-second separator,
// and timezone representation. In contrast, the default v2 behavior
// is to strictly comply with the grammar specified in RFC 3339.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func ParseTimeWithLooseRFC3339(v bool) Options {
return json.ParseTimeWithLooseRFC3339(v)
}
// ReportErrorsWithLegacySemantics specifies that Marshal and Unmarshal
// should report errors with legacy semantics:
//
// - When marshaling or unmarshaling, the returned error values are
// usually of types such as [SyntaxError], [MarshalerError],
// [UnsupportedTypeError], [UnsupportedValueError],
// [InvalidUnmarshalError], or [UnmarshalTypeError].
// In contrast, the v2 semantic is to always return errors as either
// [jsonv2.SemanticError] or [jsontext.SyntacticError].
//
// - When marshaling, if a user-defined marshal method reports an error,
// it is always wrapped in a [MarshalerError], even if the error itself
// is already a [MarshalerError], which may lead to multiple redundant
// layers of wrapping. In contrast, the v2 semantic is to
// always wrap an error within [jsonv2.SemanticError]
// unless it is already a semantic error.
//
// - When unmarshaling, if a user-defined unmarshal method reports an error,
// it is never wrapped and reported verbatim. In contrast, the v2 semantic
// is to always wrap an error within [jsonv2.SemanticError]
// unless it is already a semantic error.
//
// - When marshaling or unmarshaling, if a Go struct contains type errors
// (e.g., conflicting names or malformed field tags), then such errors
// are ignored and the Go struct uses a best-effort representation.
// In contrast, the v2 semantic is to report a runtime error.
//
// - When unmarshaling with [jsonv2.MatchCaseInsensitiveNames], if a JSON
// object name has a non-exact match with multiple Go struct fields, then
// an error is not reported and instead the first declared field is used.
// In contrast, the v2 semantic is to report a runtime error.
//
// - When unmarshaling, the syntactic structure of the JSON input
// is fully validated before performing the semantic unmarshaling
// of the JSON data into the Go value. Practically speaking,
// this means that JSON input with syntactic errors do not result
// in any mutations of the target Go value. In contrast, the v2 semantic
// is to perform a streaming decode and gradually unmarshal the JSON input
// into the target Go value, which means that the Go value may be
// partially mutated when a syntactic error is encountered.
//
// - When unmarshaling, a semantic error does not immediately terminate the
// unmarshal procedure, but rather evaluation continues.
// When unmarshal returns, only the first semantic error is reported.
// In contrast, the v2 semantic is to terminate unmarshal the moment
// an error is encountered.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func ReportErrorsWithLegacySemantics(v bool) Options {
return json.ReportErrorsWithLegacySemantics(v)
}
// StringifyWithLegacySemantics specifies that the `string` tag option
// may stringify bools and string values. It only takes effect on fields
// where the top-level type is a bool, string, numeric kind, or a pointer to
// such a kind. In contrast, the v2 default only allows the `string` tag option
// on Go types that would have otherwise serialized as a JSON number,
// and stringifies the JSON number within a JSON string. In particular,
// the v2 default does not stringify Go bools and strings.
// If [ReportErrorsWithLegacySemantics] is false,
// then incorrect usages of `string` result in a runtime error.
//
// When marshaling, such Go values are serialized as their usual JSON
// representation, but quoted within a JSON string.
// When unmarshaling, such Go values must be deserialized from a JSON string
// containing their usual JSON representation or Go number representation for
// that numeric kind.
// Note that the Go number grammar is a superset of the JSON number grammar.
// A JSON null quoted in a JSON string is a valid substitute for JSON null
// while unmarshaling into a Go value that `string` takes effect on.
// In contrast, the v2 default rejects stringified numbers outside of the
// grammar for a JSON number and also rejects a JSON null quoted in a JSON string.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func StringifyWithLegacySemantics(v bool) Options {
return json.StringifyWithLegacySemantics(v)
}
// UnmarshalArrayFromAnyLength specifies that Go arrays can be unmarshaled
// from input JSON arrays of any length. If the JSON array is too short,
// then the remaining Go array elements are zeroed. If the JSON array
// is too long, then the excess JSON array elements are skipped over.
// In contrast, the v2 default expects that Go arrays be unmarshaled
// from input JSON arrays of the exact same length.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func UnmarshalArrayFromAnyLength(v bool) Options {
return json.UnmarshalArrayFromAnyLength(v)
}
// Valid reports whether data is a valid JSON encoding.
func Valid(data []byte) bool {
return json.Valid(data)
}
// A SyntaxError is a description of a JSON syntax error.
// [Unmarshal] will return a SyntaxError if the JSON can't be parsed.
type SyntaxError = json.SyntaxError
// A Decoder reads and decodes JSON values from an input stream.
type Decoder = json.Decoder
// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
return json.NewDecoder(r)
}
// An Encoder writes JSON values to an output stream.
type Encoder = json.Encoder
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return json.NewEncoder(w)
}
// RawMessage is a raw encoded JSON value.
// It implements [Marshaler] and [Unmarshaler] and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage = json.RawMessage
// A Token holds a value of one of these types:
//
// - [Delim], for the four JSON delimiters [ ] { }
// - bool, for JSON booleans
// - float64, for JSON numbers
// - [Number], for JSON numbers
// - string, for JSON string literals
// - nil, for JSON null
type Token = json.Token
// A Delim is a JSON array or object delimiter, one of [ ] { or }.
type Delim = json.Delim
+271
View File
@@ -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}
}
+262
View File
@@ -0,0 +1,262 @@
// 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
// Package json implements encoding and decoding of JSON as defined in
// RFC 7159. The mapping between JSON and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
//
// See [JSON and Go] for an introduction to this package.
//
// # Security Considerations
//
// See the "Security Considerations" section in [encoding/json/v2].
//
// For historical reasons, the default behavior of v1 [encoding/json]
// unfortunately operates with less secure defaults.
// New usages of JSON in Go are encouraged to use [encoding/json/v2] instead.
//
// [JSON and Go]: https://go.dev/blog/json
package json
import (
"reflect"
"strconv"
jsonv2 "github.com/go-json-experiment/json"
)
// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
//
// The input value is encoded as JSON according the following rules:
//
// - If the value type implements [jsonv2.MarshalerTo],
// then the MarshalJSONTo method is called to encode the value.
// If the method returns [errors.ErrUnsupported],
// then the input is encoded according to subsequent rules.
//
// - If the value type implements [Marshaler],
// then the MarshalJSON method is called to encode the value.
//
// - If the value type implements [encoding.TextAppender],
// then the AppendText method is called to encode the value and
// subsequently encode its result as a JSON string.
//
// - If the value type implements [encoding.TextMarshaler],
// then the MarshalText method is called to encode the value and
// subsequently encode its result as a JSON string.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as JSON booleans.
//
// Floating point, integer, and [Number] values encode as JSON numbers.
// NaN and +/-Inf values will return an [UnsupportedValueError].
//
// String values encode as JSON strings coerced to valid UTF-8,
// replacing invalid bytes with the Unicode replacement rune.
// So that the JSON will be safe to embed inside HTML <script> tags,
// the string is encoded using [HTMLEscape],
// which replaces "<", ">", "&", U+2028, and U+2029 are escaped
// to "\u003c","\u003e", "\u0026", "\u2028", and "\u2029".
// This replacement can be disabled when using an [Encoder],
// by calling [Encoder.SetEscapeHTML](false).
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string, and a nil slice
// encodes as the null JSON value.
//
// Struct values encode as JSON objects.
// Each exported struct field becomes a member of the object, using the
// field name as the object key, unless the field is omitted for one of the
// reasons given below.
//
// The encoding of each struct field can be customized by the format string
// stored under the "json" key in the struct field's tag.
// The format string gives the name of the field, possibly followed by a
// comma-separated list of options. The name may be empty in order to
// specify options without overriding the default field name.
//
// The "omitempty" option specifies that the field should be omitted
// from the encoding if the field has an empty value, defined as
// false, 0, a nil pointer, a nil interface value, and any array,
// slice, map, or string of length zero.
//
// As a special case, if the field tag is "-", the field is always omitted.
// JSON names containing commas or quotes, or names identical to "" or "-",
// can be specified using a single-quoted string literal, where the syntax
// is identical to the Go grammar for a double-quoted string literal,
// but instead uses single quotes as the delimiters.
//
// Examples of struct field tags and their meanings:
//
// // Field appears in JSON as key "myName".
// Field int `json:"myName"`
//
// // Field appears in JSON as key "myName" and
// // the field is omitted from the object if its value is empty,
// // as defined above.
// Field int `json:"myName,omitempty"`
//
// // Field appears in JSON as key "Field" (the default), but
// // the field is skipped if empty.
// // Note the leading comma.
// Field int `json:",omitempty"`
//
// // Field is ignored by this package.
// Field int `json:"-"`
//
// // Field appears in JSON as key "-".
// Field int `json:"'-'"`
//
// The "omitzero" option specifies that the field should be omitted
// from the encoding if the field has a zero value, according to rules:
//
// 1) If the field type has an "IsZero() bool" method, that will be used to
// determine whether the value is zero.
//
// 2) Otherwise, the value is zero if it is the zero value for its type.
//
// If both "omitempty" and "omitzero" are specified, the field will be omitted
// if the value is either empty or zero (or both).
//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
// when communicating with JavaScript programs:
//
// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, and ASCII punctuation except quotation
// marks, backslash, and comma.
//
// Embedded struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules amended
// as described in the next paragraph.
// An anonymous struct field with a name given in its JSON tag is treated as
// having that name, rather than being anonymous.
// An anonymous struct field of interface type is treated the same as having
// that type as its name, rather than being anonymous.
//
// The Go visibility rules for struct fields are amended for JSON when
// deciding which field to marshal or unmarshal. If there are
// multiple fields at the same level, and that level is the least
// nested (and would therefore be the nesting level selected by the
// usual Go rules), the following extra rules apply:
//
// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
// even if there are multiple untagged fields that would otherwise conflict.
//
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
//
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
//
// Handling of anonymous struct fields is new in Go 1.1.
// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of
// an anonymous struct field in both current and earlier versions, give the field
// a JSON tag of "-".
//
// Map values encode as JSON objects. The map's key type must either be a
// string, an integer type, or implement [encoding.TextMarshaler]. The map keys
// are sorted and used as JSON object keys by applying the following rules,
// subject to the UTF-8 coercion described for string values above:
// - keys of any string type are used directly
// - keys that implement [encoding.TextMarshaler] are marshaled
// - integer keys are converted to strings
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null JSON value.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON value.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an [UnsupportedTypeError].
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an error.
func Marshal(v any) ([]byte, error) {
return jsonv2.Marshal(v, DefaultOptionsV1())
}
// MarshalIndent is like [Marshal] but applies [Indent] to format the output.
// Each JSON element in the output will begin on a new line beginning with prefix
// followed by one or more copies of indent according to the indentation nesting.
func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
b, err := Marshal(v)
if err != nil {
return nil, err
}
b, err = appendIndent(nil, b, prefix, indent)
if err != nil {
return nil, err
}
return b, nil
}
// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler = jsonv2.Marshaler
// 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 "json: unsupported type: " + e.Type.String()
}
// An UnsupportedValueError is returned by [Marshal] when attempting
// to encode an unsupported value.
type UnsupportedValueError struct {
Value reflect.Value
Str string
}
func (e *UnsupportedValueError) Error() string {
return "json: unsupported value: " + e.Str
}
// 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 "json: invalid UTF-8 in string: " + strconv.Quote(e.S)
}
// A MarshalerError represents an error from calling a
// [Marshaler.MarshalJSON] or [encoding.TextMarshaler.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 "json: error calling " + srcFunc +
" for type " + e.Type.String() +
": " + e.Err.Error()
}
// Unwrap returns the underlying error.
func (e *MarshalerError) Unwrap() error { return e.Err }
+131
View File
@@ -0,0 +1,131 @@
// 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
package json
import (
"bytes"
"strings"
"github.com/go-json-experiment/json/jsontext"
)
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
// so that the JSON will be safe to embed inside HTML <script> tags.
// For historical reasons, web browsers don't honor standard HTML
// escaping within <script> tags, so an alternative JSON encoding must be used.
func HTMLEscape(dst *bytes.Buffer, src []byte) {
dst.Grow(len(src))
dst.Write(appendHTMLEscape(dst.AvailableBuffer(), src))
}
func appendHTMLEscape(dst, src []byte) []byte {
const hex = "0123456789abcdef"
// The characters can only appear in string literals,
// so just scan the string one byte at a time.
start := 0
for i, c := range src {
if c == '<' || c == '>' || c == '&' {
dst = append(dst, src[start:i]...)
dst = append(dst, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xF])
start = i + 1
}
// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
dst = append(dst, src[start:i]...)
dst = append(dst, '\\', 'u', '2', '0', '2', hex[src[i+2]&0xF])
start = i + len("\u2029")
}
}
return append(dst, src[start:]...)
}
// Compact appends to dst the JSON-encoded src with
// insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error {
dst.Grow(len(src))
b := dst.AvailableBuffer()
b, err := jsontext.AppendFormat(b, src,
ReportErrorsWithLegacySemantics(true),
jsontext.AllowDuplicateNames(true),
jsontext.AllowInvalidUTF8(true),
jsontext.PreserveRawStrings(true))
if err != nil {
return transformSyntacticError(err)
}
dst.Write(b)
return nil
}
// indentGrowthFactor specifies the growth factor of indenting JSON input.
// Empirically, the growth factor was measured to be between 1.4x to 1.8x
// for some set of compacted JSON with the indent being a single tab.
// Specify a growth factor slightly larger than what is observed
// to reduce probability of allocation in appendIndent.
// A factor no higher than 2 ensures that wasted space never exceeds 50%.
const indentGrowthFactor = 2
// Indent appends to dst an indented form of the JSON-encoded src.
// Each element in a JSON object or array begins on a new,
// indented line beginning with prefix followed by one or more
// copies of indent according to the indentation nesting.
// The data appended to dst does not begin with the prefix nor
// any indentation, to make it easier to embed inside other formatted JSON data.
// Although leading space characters (space, tab, carriage return, newline)
// at the beginning of src are dropped, trailing space characters
// at the end of src are preserved and copied to dst.
// For example, if src has no trailing spaces, neither will dst;
// if src ends in a trailing newline, so will dst.
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
dst.Grow(indentGrowthFactor * len(src))
b := dst.AvailableBuffer()
b, err := appendIndent(b, src, prefix, indent)
dst.Write(b)
return err
}
func appendIndent(dst, src []byte, prefix, indent string) ([]byte, error) {
// In v2, only spaces and tabs are allowed, while v1 allowed any character.
dstLen := len(dst)
if len(strings.Trim(prefix, " \t"))+len(strings.Trim(indent, " \t")) > 0 {
// Use placeholder spaces of correct length, and replace afterwards.
invalidPrefix, invalidIndent := prefix, indent
prefix = strings.Repeat(" ", len(prefix))
indent = strings.Repeat(" ", len(indent))
defer func() {
b := dst[dstLen:]
for i := bytes.IndexByte(b, '\n'); i >= 0; i = bytes.IndexByte(b, '\n') {
b = b[i+len("\n"):]
n := len(b) - len(bytes.TrimLeft(b, " ")) // len(prefix)+n*len(indent)
spaces := b[:n]
spaces = spaces[copy(spaces, invalidPrefix):]
for len(spaces) > 0 {
spaces = spaces[copy(spaces, invalidIndent):]
}
b = b[n:]
}
}()
}
dst, err := jsontext.AppendFormat(dst, src,
ReportErrorsWithLegacySemantics(true),
jsontext.AllowDuplicateNames(true),
jsontext.AllowInvalidUTF8(true),
jsontext.PreserveRawStrings(true),
jsontext.Multiline(true),
jsontext.WithIndentPrefix(prefix),
jsontext.WithIndent(indent))
if err != nil {
return dst[:dstLen], transformSyntacticError(err)
}
// In v2, trailing whitespace is discarded, while v1 preserved it.
if n := len(src) - len(bytes.TrimRight(src, " \n\r\t")); n > 0 {
dst = append(dst, src[len(src)-n:]...)
}
return dst, nil
}
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2024 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
package json
import (
"fmt"
"reflect"
"strconv"
"strings"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/internal"
"github.com/go-json-experiment/json/jsontext"
)
// Inject functionality into v2 to properly handle v1 types.
func init() {
internal.TransformMarshalError = transformMarshalError
internal.TransformUnmarshalError = transformUnmarshalError
internal.NewMarshalerError = func(val any, err error, funcName string) error {
return &MarshalerError{reflect.TypeOf(val), err, funcName}
}
internal.NewRawNumber = func() any { return new(Number) }
internal.RawNumberOf = func(b []byte) any { return Number(b) }
}
func transformMarshalError(root any, err error) error {
// Historically, errors returned from Marshal methods were wrapped
// in a [MarshalerError]. This is directly performed by the v2 package
// via the injected [internal.NewMarshalerError] constructor
// while operating under [ReportErrorsWithLegacySemantics].
// Note that errors from a Marshal method were always wrapped,
// even if wrapped for multiple layers.
if err, ok := err.(*jsonv2.SemanticError); err != nil {
if err.Err == nil {
// Historically, this was only reported for unserializable types
// like complex numbers, channels, functions, and unsafe.Pointers.
return &UnsupportedTypeError{Type: err.GoType}
} else {
// Historically, this was only reported for NaN or ±Inf values
// and cycles detected in the value.
// The Value field used to be populated with the reflect.Value,
// but this is no longer supported.
errStr := err.Err.Error()
if err.Err == internal.ErrCycle && err.GoType != nil {
errStr += " via " + err.GoType.String()
}
errStr = strings.TrimPrefix(errStr, "unsupported value: ")
return &UnsupportedValueError{Str: errStr}
}
} else if ok {
return (*UnsupportedValueError)(nil)
}
if err, _ := err.(*MarshalerError); err != nil {
err.Err = transformSyntacticError(err.Err)
return err
}
return transformSyntacticError(err)
}
func transformUnmarshalError(root any, err error) error {
// Historically, errors from Unmarshal methods were never wrapped and
// returned verbatim while operating under [ReportErrorsWithLegacySemantics].
if err, ok := err.(*jsonv2.SemanticError); err != nil {
if err.Err == internal.ErrNonNilReference {
return &InvalidUnmarshalError{err.GoType}
}
if err.Err == jsonv2.ErrUnknownName {
return fmt.Errorf("json: unknown field %q", err.JSONPointer.LastToken())
}
if err.Err == internal.ErrNilInterface {
err.Err = nil // non-descriptive for historical reasons
}
// Historically, UnmarshalTypeError has always been inconsistent
// about how it reported position information.
//
// The Struct field now points to the root type,
// rather than some intermediate struct in the path.
// This better matches the original intent of the field based
// on how the Error message was formatted.
//
// For a representation closer to the historical representation,
// we switch the '/'-delimited representation of a JSON pointer
// to use a '.'-delimited representation. This may be ambiguous,
// but the prior representation was always ambiguous as well.
// Users that care about precise positions should use v2 errors
// by disabling [ReportErrorsWithLegacySemantics].
//
// The introduction of an Err field is new to the v1-to-v2 migration
// and allows us to preserve stronger error information
// that may be surfaced by the v2 package.
//
// See https://go.dev/issue/43126
var value string
switch err.JSONKind {
case 'n', '"', '0':
value = err.JSONKind.String()
case 'f', 't':
value = "bool"
case '[', ']':
value = "array"
case '{', '}':
value = "object"
}
if len(err.JSONValue) > 0 {
isStrconvError := err.Err == strconv.ErrRange || err.Err == strconv.ErrSyntax
isNumericKind := func(t reflect.Type) bool {
if t == nil {
return false
}
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64:
return true
}
return false
}
if isStrconvError && isNumericKind(err.GoType) {
value = "number"
if err.JSONKind == '"' {
err.JSONValue, _ = jsontext.AppendUnquote(nil, err.JSONValue)
}
err.Err = nil
}
value += " " + string(err.JSONValue)
}
var rootName string
if t := reflect.TypeOf(root); t != nil && err.JSONPointer != "" {
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
rootName = t.Name()
}
fieldPath := string(err.JSONPointer)
fieldPath = strings.TrimPrefix(fieldPath, "/")
fieldPath = strings.ReplaceAll(fieldPath, "/", ".")
return &UnmarshalTypeError{
Value: value,
Type: err.GoType,
Offset: err.ByteOffset,
Struct: rootName,
Field: fieldPath,
Err: transformSyntacticError(err.Err),
}
} else if ok {
return (*UnmarshalTypeError)(nil)
}
return transformSyntacticError(err)
}
+553
View File
@@ -0,0 +1,553 @@
// Copyright 2023 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
// Migrating to v2
//
// This package (i.e., [encoding/json]) is now formally known as the v1 package
// since a v2 package now exists at [encoding/json/v2].
// All the behavior of the v1 package is implemented in terms of
// the v2 package with the appropriate set of options specified that
// preserve the historical behavior of v1.
//
// The [jsonv2.Marshal] function is the newer equivalent of v1 [Marshal].
// The [jsonv2.Unmarshal] function is the newer equivalent of v1 [Unmarshal].
// The v2 functions have the same calling signature as the v1 equivalent
// except that they take in variadic [Options] arguments that can be specified
// to alter the behavior of marshal or unmarshal. Both v1 and v2 generally
// behave in similar ways, but there are some notable differences.
//
// The following is a list of differences between v1 and v2:
//
// - In v1, JSON object members are unmarshaled into a Go struct using a
// case-insensitive name match with the JSON name of the fields.
// In contrast, v2 matches fields using an exact, case-sensitive match.
// The [jsonv2.MatchCaseInsensitiveNames] and [MatchCaseSensitiveDelimiter]
// options control this behavior difference. To explicitly specify a Go struct
// field to use a particular name matching scheme, either the `case:ignore`
// or the `case:strict` field option can be specified.
// Field-specified options take precedence over caller-specified options.
//
// - In v1, when marshaling a Go struct, a field marked as `omitempty`
// is omitted if the field value is an "empty" Go value, which is defined as
// false, 0, a nil pointer, a nil interface value, and
// any empty array, slice, map, or string. In contrast, v2 redefines
// `omitempty` to omit a field if it encodes as an "empty" JSON value,
// which is defined as a JSON null, or an empty JSON string, object, or array.
// The [OmitEmptyWithLegacySemantics] option controls this behavior difference.
// Note that `omitempty` behaves identically in both v1 and v2 for a
// Go array, slice, map, or string (assuming no user-defined MarshalJSON method
// overrides the default representation). Existing usages of `omitempty` on a
// Go bool, number, pointer, or interface value should migrate to specifying
// `omitzero` instead (which is identically supported in both v1 and v2).
//
// - In v1, a Go struct field marked as `string` can be used to quote a
// Go string, bool, number, or pointer to such as a JSON string.
// In contrast, v2 restricts the `string` option to only quote a value
// that would normally be represented as a JSON number,
// but also expands support for it to operate with any Go type
// that would normally be represented as a JSON number.
// The [StringifyWithLegacySemantics] option controls this behavior difference.
//
// - In v1, a nil Go slice or Go map is marshaled as a JSON null.
// In contrast, v2 marshals a nil Go slice or Go map as
// an empty JSON array or JSON object, respectively.
// The [jsonv2.FormatNilSliceAsNull] and [jsonv2.FormatNilMapAsNull] options
// control this behavior difference.
//
// - In v1, a Go array may be unmarshaled from a JSON array of any length.
// In contrast, in v2 a Go array must be unmarshaled from a JSON array
// of the same length, otherwise it results in an error.
// The [UnmarshalArrayFromAnyLength] option controls this behavior difference.
//
// - In v1, a Go byte array is represented as a JSON array of JSON numbers.
// In contrast, in v2 a Go byte array is represented as a Base64-encoded JSON string.
// The [FormatByteArrayAsArray] option controls this behavior difference.
//
// - In v1, MarshalJSON methods declared on a pointer receiver are only called
// if the Go value is addressable. In contrast, in v2 a MarshalJSON method
// is always callable regardless of addressability.
// The [CallMethodsWithLegacySemantics] option controls this behavior difference.
//
// - In v1, MarshalJSON and UnmarshalJSON methods are never called for Go map keys.
// In contrast, in v2 a MarshalJSON or UnmarshalJSON method is eligible for
// being called for Go map keys.
// The [CallMethodsWithLegacySemantics] option controls this behavior difference.
//
// - In v1, a Go map is marshaled in a deterministic order.
// In contrast, in v2 a Go map is marshaled in a non-deterministic order.
// The [jsonv2.Deterministic] option controls this behavior difference.
//
// - In v1, JSON strings are encoded with HTML-specific or JavaScript-specific
// characters being escaped. In contrast, in v2 JSON strings use the minimal
// encoding and only escape if required by the JSON grammar.
// The [jsontext.EscapeForHTML] and [jsontext.EscapeForJS] options
// control this behavior difference.
//
// - In v1, bytes of invalid UTF-8 within a string are silently replaced with
// the Unicode replacement character. In contrast, in v2 the presence of
// invalid UTF-8 results in an error. The [jsontext.AllowInvalidUTF8] option
// controls this behavior difference.
//
// - In v1, a JSON object with duplicate names is permitted.
// In contrast, in v2 a JSON object with duplicate names results in an error.
// The [jsontext.AllowDuplicateNames] option controls this behavior difference.
//
// - In v1, when unmarshaling a JSON null into a non-empty Go value it will
// inconsistently either zero out the value or do nothing.
// In contrast, in v2 unmarshaling a JSON null will consistently and always
// zero out the underlying Go value. The [MergeWithLegacySemantics] option
// controls this behavior difference.
//
// - In v1, when unmarshaling a JSON value into a non-zero Go value,
// it merges into the original Go value for array elements, slice elements,
// struct fields (but not map values),
// pointer values, and interface values (only if a non-nil pointer).
// In contrast, in v2 unmarshal merges into the Go value
// for struct fields, map values, pointer values, and interface values.
// In general, the v2 semantic merges when unmarshaling a JSON object,
// otherwise it replaces the value. The [MergeWithLegacySemantics] option
// controls this behavior difference.
//
// - In v1, a [time.Duration] is represented as a JSON number containing
// the decimal number of nanoseconds. In contrast, in v2 a [time.Duration]
// has no default representation and results in a runtime error.
// The [FormatDurationAsNano] option controls this behavior difference.
//
// - In v1, errors are never reported at runtime for Go struct types
// that have some form of structural error (e.g., a malformed tag option).
// In contrast, v2 reports a runtime error for Go types that are invalid
// as they relate to JSON serialization. For example, a Go struct
// with only unexported fields cannot be serialized.
// The [ReportErrorsWithLegacySemantics] option controls this behavior difference.
//
// As mentioned, the entirety of v1 is implemented in terms of v2,
// where options are implicitly specified to opt into legacy behavior.
// For example, [Marshal] directly calls [jsonv2.Marshal] with [DefaultOptionsV1].
// Similarly, [Unmarshal] directly calls [jsonv2.Unmarshal] with [DefaultOptionsV1].
// The [DefaultOptionsV1] option represents the set of all options that specify
// default v1 behavior.
//
// For many of the behavior differences, there are Go struct field options
// that the author of a Go type can specify to control the behavior such that
// the type is represented identically in JSON under either v1 or v2 semantics.
//
// The availability of [DefaultOptionsV1] and [jsonv2.DefaultOptionsV2],
// where later options take precedence over former options allows for
// a gradual migration from v1 to v2. For example:
//
// - jsonv1.Marshal(v)
// uses default v1 semantics.
//
// - jsonv2.Marshal(v, jsonv1.DefaultOptionsV1())
// is semantically equivalent to jsonv1.Marshal
// and thus uses default v1 semantics.
//
// - jsonv2.Marshal(v, jsonv1.DefaultOptionsV1(), jsontext.AllowDuplicateNames(false))
// uses mostly v1 semantics, but opts into one particular v2-specific behavior.
//
// - jsonv2.Marshal(v, jsonv1.CallMethodsWithLegacySemantics(true))
// uses mostly v2 semantics, but opts into one particular v1-specific behavior.
//
// - jsonv2.Marshal(v, ..., jsonv2.DefaultOptionsV2())
// is semantically equivalent to jsonv2.Marshal since
// jsonv2.DefaultOptionsV2 overrides any options specified earlier
// and thus uses default v2 semantics.
//
// - jsonv2.Marshal(v)
// uses default v2 semantics.
//
// All new usages of "json" in Go should use the v2 package,
// but the v1 package will forever remain supported.
//
// See the [encoding/json/v2 Migration Guide] for additional detail on migration approaches.
//
// [encoding/json/v2 Migration Guide]: https://go.dev/doc/jsonv2-migration
package json
// TODO(https://go.dev/issue/71631): Update the "Migrating to v2" documentation
// with default v2 behavior for [time.Duration].
import (
"encoding"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/internal/jsonflags"
"github.com/go-json-experiment/json/internal/jsonopts"
"github.com/go-json-experiment/json/jsontext"
)
// Reference encoding, jsonv2, and jsontext packages to assist pkgsite
// in being able to hotlink references to those packages.
var (
_ encoding.TextMarshaler
_ encoding.TextUnmarshaler
_ jsonv2.Options
_ jsontext.Options
)
// Options are a set of options to configure the v2 "json" package
// to operate with v1 semantics for particular features.
// Values of this type can be passed to v2 functions like
// [jsonv2.Marshal] or [jsonv2.Unmarshal].
// Instead of referencing this type, use [jsonv2.Options].
//
// See the "Migrating to v2" section for guidance on how to migrate usage
// of "json" from using v1 to using v2 instead.
type Options = jsonopts.Options
// DefaultOptionsV1 is the full set of all options that define v1 semantics.
// It is equivalent to the following boolean options being set to true:
//
// - [CallMethodsWithLegacySemantics]
// - [FormatByteArrayAsArray]
// - [FormatBytesWithLegacySemantics]
// - [FormatDurationAsNano]
// - [MatchCaseSensitiveDelimiter]
// - [MergeWithLegacySemantics]
// - [OmitEmptyWithLegacySemantics]
// - [ParseBytesWithLooseRFC4648]
// - [ParseTimeWithLooseRFC3339]
// - [ReportErrorsWithLegacySemantics]
// - [StringifyWithLegacySemantics]
// - [UnmarshalArrayFromAnyLength]
// - [jsonv2.Deterministic]
// - [jsonv2.FormatNilMapAsNull]
// - [jsonv2.FormatNilSliceAsNull]
// - [jsonv2.MatchCaseInsensitiveNames]
// - [jsontext.AllowDuplicateNames]
// - [jsontext.AllowInvalidUTF8]
// - [jsontext.EscapeForHTML]
// - [jsontext.EscapeForJS]
// - [jsontext.PreserveRawStrings]
//
// All other options are not present.
//
// The [Marshal] and [Unmarshal] functions in this package are
// semantically identical to calling the v2 equivalents with this option:
//
// jsonv2.Marshal(v, jsonv1.DefaultOptionsV1())
// jsonv2.Unmarshal(b, v, jsonv1.DefaultOptionsV1())
func DefaultOptionsV1() Options {
return &jsonopts.DefaultOptionsV1
}
// CallMethodsWithLegacySemantics specifies that calling of type-provided
// marshal and unmarshal methods follow legacy semantics:
//
// - When marshaling, a marshal method declared on a pointer receiver
// is only called if the Go value is addressable.
// Values obtained from an interface or map element are not addressable.
// Values obtained from a pointer or slice element are addressable.
// Values obtained from an array element or struct field inherit
// the addressability of the parent. In contrast, the v2 semantic
// is to always call marshal methods regardless of addressability.
//
// - When marshaling or unmarshaling, the [Marshaler] or [Unmarshaler]
// methods are ignored for map keys. However, [encoding.TextMarshaler]
// or [encoding.TextUnmarshaler] are still callable.
// In contrast, the v2 semantic is to serialize map keys
// like any other value (with regard to calling methods),
// which may include calling [Marshaler] or [Unmarshaler] methods,
// where it is the implementation's responsibility to represent the
// Go value as a JSON string (as required for JSON object names).
//
// - When marshaling, if a map key value implements a marshal method
// and is a nil pointer, then it is serialized as an empty JSON string.
// In contrast, the v2 semantic is to report an error.
//
// - When marshaling, if an interface type implements a marshal method
// and the interface value is a nil pointer to a concrete type,
// then the marshal method is always called.
// In contrast, the v2 semantic is to never directly call methods
// on interface values and to instead defer evaluation based upon
// the underlying concrete value. Similar to non-interface values,
// marshal methods are not called on nil pointers and
// are instead serialized as a JSON null.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func CallMethodsWithLegacySemantics(v bool) Options {
if v {
return jsonflags.CallMethodsWithLegacySemantics | 1
} else {
return jsonflags.CallMethodsWithLegacySemantics | 0
}
}
// FormatByteArrayAsArray specifies that a Go [N]byte is
// formatted as a normal Go array in contrast to the v2 default of
// formatting [N]byte as using binary data encoding (RFC 4648).
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func FormatByteArrayAsArray(v bool) Options {
if v {
return jsonflags.FormatByteArrayAsArray | 1
} else {
return jsonflags.FormatByteArrayAsArray | 0
}
}
// FormatBytesWithLegacySemantics specifies that handling of
// []~byte and [N]~byte types follow legacy semantics:
//
// - A Go []~byte is to be treated as using some form of
// binary data encoding (RFC 4648) in contrast to the v2 default
// of only treating []byte as such. In particular, v2 does not
// treat slices of named byte types as representing binary data.
//
// - When marshaling, if a named byte implements a marshal method,
// then the slice is serialized as a JSON array of elements,
// each of which call the marshal method.
//
// - When unmarshaling, if the input is a JSON array,
// then unmarshal into the []~byte as if it were a normal Go slice.
// In contrast, the v2 default is to report an error unmarshaling
// a JSON array when expecting some form of binary data encoding.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func FormatBytesWithLegacySemantics(v bool) Options {
if v {
return jsonflags.FormatBytesWithLegacySemantics | 1
} else {
return jsonflags.FormatBytesWithLegacySemantics | 0
}
}
// FormatDurationAsNano specifies that a [time.Duration] is
// formatted as a JSON number representing the number of nanoseconds
// in contrast to the v2 default of reporting an error.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func FormatDurationAsNano(v bool) Options {
// TODO(https://go.dev/issue/71631): Update documentation with v2 behavior.
if v {
return jsonflags.FormatDurationAsNano | 1
} else {
return jsonflags.FormatDurationAsNano | 0
}
}
// MatchCaseSensitiveDelimiter specifies that underscores and dashes are
// not to be ignored when performing case-insensitive name matching which
// occurs under [jsonv2.MatchCaseInsensitiveNames] or the `case:ignore` tag option.
// Thus, case-insensitive name matching is identical to [strings.EqualFold].
// Use of this option diminishes the ability of case-insensitive matching
// to be able to match common case variants (e.g., "foo_bar" with "fooBar").
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func MatchCaseSensitiveDelimiter(v bool) Options {
if v {
return jsonflags.MatchCaseSensitiveDelimiter | 1
} else {
return jsonflags.MatchCaseSensitiveDelimiter | 0
}
}
// MergeWithLegacySemantics specifies that unmarshaling into a non-zero
// Go value follows legacy semantics:
//
// - When unmarshaling a JSON null, this preserves the original Go value
// if the kind is a bool, int, uint, float, string, array, or struct.
// Otherwise, it zeros the Go value.
// In contrast, the default v2 behavior is to consistently and always
// zero the Go value when unmarshaling a JSON null into it.
//
// - When unmarshaling a JSON value other than null, this merges into
// the original Go value for array elements, slice elements,
// struct fields (but not map values),
// pointer values, and interface values (only if a non-nil pointer).
// For slices, it will merge into the pre-existing value of slice elements
// even for those past the slice length. If the original slice length
// was longer than the JSON array, then it is truncated to match.
// In contrast, the default v2 behavior is to merge into the Go value
// for struct fields, map values, pointer values, and interface values.
// In general, the v2 semantic merges when unmarshaling a JSON object,
// otherwise it replaces the original value.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func MergeWithLegacySemantics(v bool) Options {
if v {
return jsonflags.MergeWithLegacySemantics | 1
} else {
return jsonflags.MergeWithLegacySemantics | 0
}
}
// OmitEmptyWithLegacySemantics specifies that the `omitempty` tag option
// follows a definition of empty where a field is omitted if the Go value is
// false, 0, a nil pointer, a nil interface value,
// or any empty array, slice, map, or string.
// This overrides the v2 semantic where a field is empty if the value
// marshals as a JSON null or an empty JSON string, object, or array.
//
// The v1 and v2 definitions of `omitempty` are practically the same for
// Go strings, slices, arrays, and maps. Usages of `omitempty` on
// Go bools, ints, uints, floats, pointers, and interfaces should migrate to use
// the `omitzero` tag option, which omits a field if it is the zero Go value.
//
// This only affects marshaling and is ignored when unmarshaling.
// The v1 default is true.
func OmitEmptyWithLegacySemantics(v bool) Options {
if v {
return jsonflags.OmitEmptyWithLegacySemantics | 1
} else {
return jsonflags.OmitEmptyWithLegacySemantics | 0
}
}
// ParseBytesWithLooseRFC4648 specifies that when parsing
// binary data encoded as "base32" or "base64",
// to ignore the presence of '\r' and '\n' characters.
// In contrast, the v2 default is to report an error in order to be
// strictly compliant with RFC 4648, section 3.3,
// which specifies that non-alphabet characters must be rejected.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func ParseBytesWithLooseRFC4648(v bool) Options {
if v {
return jsonflags.ParseBytesWithLooseRFC4648 | 1
} else {
return jsonflags.ParseBytesWithLooseRFC4648 | 0
}
}
// ParseTimeWithLooseRFC3339 specifies that a [time.Time]
// parses according to loose adherence to RFC 3339.
// In particular, it permits historically incorrect representations,
// allowing for deviations in hour format, sub-second separator,
// and timezone representation. In contrast, the default v2 behavior
// is to strictly comply with the grammar specified in RFC 3339.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func ParseTimeWithLooseRFC3339(v bool) Options {
if v {
return jsonflags.ParseTimeWithLooseRFC3339 | 1
} else {
return jsonflags.ParseTimeWithLooseRFC3339 | 0
}
}
// ReportErrorsWithLegacySemantics specifies that Marshal and Unmarshal
// should report errors with legacy semantics:
//
// - When marshaling or unmarshaling, the returned error values are
// usually of types such as [SyntaxError], [MarshalerError],
// [UnsupportedTypeError], [UnsupportedValueError],
// [InvalidUnmarshalError], or [UnmarshalTypeError].
// In contrast, the v2 semantic is to always return errors as either
// [jsonv2.SemanticError] or [jsontext.SyntacticError].
//
// - When marshaling, if a user-defined marshal method reports an error,
// it is always wrapped in a [MarshalerError], even if the error itself
// is already a [MarshalerError], which may lead to multiple redundant
// layers of wrapping. In contrast, the v2 semantic is to
// always wrap an error within [jsonv2.SemanticError]
// unless it is already a semantic error.
//
// - When unmarshaling, if a user-defined unmarshal method reports an error,
// it is never wrapped and reported verbatim. In contrast, the v2 semantic
// is to always wrap an error within [jsonv2.SemanticError]
// unless it is already a semantic error.
//
// - When marshaling or unmarshaling, if a Go struct contains type errors
// (e.g., conflicting names or malformed field tags), then such errors
// are ignored and the Go struct uses a best-effort representation.
// In contrast, the v2 semantic is to report a runtime error.
//
// - When unmarshaling with [jsonv2.MatchCaseInsensitiveNames], if a JSON
// object name has a non-exact match with multiple Go struct fields, then
// an error is not reported and instead the first declared field is used.
// In contrast, the v2 semantic is to report a runtime error.
//
// - When unmarshaling, the syntactic structure of the JSON input
// is fully validated before performing the semantic unmarshaling
// of the JSON data into the Go value. Practically speaking,
// this means that JSON input with syntactic errors do not result
// in any mutations of the target Go value. In contrast, the v2 semantic
// is to perform a streaming decode and gradually unmarshal the JSON input
// into the target Go value, which means that the Go value may be
// partially mutated when a syntactic error is encountered.
//
// - When unmarshaling, a semantic error does not immediately terminate the
// unmarshal procedure, but rather evaluation continues.
// When unmarshal returns, only the first semantic error is reported.
// In contrast, the v2 semantic is to terminate unmarshal the moment
// an error is encountered.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func ReportErrorsWithLegacySemantics(v bool) Options {
if v {
return jsonflags.ReportErrorsWithLegacySemantics | 1
} else {
return jsonflags.ReportErrorsWithLegacySemantics | 0
}
}
// StringifyWithLegacySemantics specifies that the `string` tag option
// may stringify bools and string values. It only takes effect on fields
// where the top-level type is a bool, string, numeric kind, or a pointer to
// such a kind. In contrast, the v2 default only allows the `string` tag option
// on Go types that would have otherwise serialized as a JSON number,
// and stringifies the JSON number within a JSON string. In particular,
// the v2 default does not stringify Go bools and strings.
// If [ReportErrorsWithLegacySemantics] is false,
// then incorrect usages of `string` result in a runtime error.
//
// When marshaling, such Go values are serialized as their usual JSON
// representation, but quoted within a JSON string.
// When unmarshaling, such Go values must be deserialized from a JSON string
// containing their usual JSON representation or Go number representation for
// that numeric kind.
// Note that the Go number grammar is a superset of the JSON number grammar.
// A JSON null quoted in a JSON string is a valid substitute for JSON null
// while unmarshaling into a Go value that `string` takes effect on.
// In contrast, the v2 default rejects stringified numbers outside of the
// grammar for a JSON number and also rejects a JSON null quoted in a JSON string.
//
// This affects either marshaling or unmarshaling.
// The v1 default is true.
func StringifyWithLegacySemantics(v bool) Options {
if v {
return jsonflags.StringifyWithLegacySemantics | 1
} else {
return jsonflags.StringifyWithLegacySemantics | 0
}
}
// UnmarshalArrayFromAnyLength specifies that Go arrays can be unmarshaled
// from input JSON arrays of any length. If the JSON array is too short,
// then the remaining Go array elements are zeroed. If the JSON array
// is too long, then the excess JSON array elements are skipped over.
// In contrast, the v2 default expects that Go arrays be unmarshaled
// from input JSON arrays of the exact same length.
//
// This only affects unmarshaling and is ignored when marshaling.
// The v1 default is true.
func UnmarshalArrayFromAnyLength(v bool) Options {
if v {
return jsonflags.UnmarshalArrayFromAnyLength | 1
} else {
return jsonflags.UnmarshalArrayFromAnyLength | 0
}
}
// unmarshalAnyWithRawNumber specifies that unmarshaling a JSON number into
// an empty Go interface should use the Number type instead of a float64.
func unmarshalAnyWithRawNumber(v bool) Options {
if v {
return jsonflags.UnmarshalAnyWithRawNumber | 1
} else {
return jsonflags.UnmarshalAnyWithRawNumber | 0
}
}
+91
View File
@@ -0,0 +1,91 @@
// 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
package json
import (
"errors"
"io"
"strings"
"github.com/go-json-experiment/json/internal"
"github.com/go-json-experiment/json/internal/jsonflags"
"github.com/go-json-experiment/json/jsontext"
)
// export exposes internal functionality of the "jsontext" package.
var export = jsontext.Internal.Export(&internal.AllowInternalUse)
// Valid reports whether data is a valid JSON encoding.
func Valid(data []byte) bool {
return checkValid(data) == nil
}
func checkValid(data []byte) error {
d := export.GetBufferedDecoder(data)
defer export.PutBufferedDecoder(d)
xd := export.Decoder(d)
xd.Struct.Flags.Set(jsonflags.ReportErrorsWithLegacySemantics | jsonflags.AllowDuplicateNames | jsonflags.AllowInvalidUTF8 | 1)
if _, err := d.ReadValue(); err != nil {
if err == io.EOF {
offset := d.InputOffset() + int64(len(d.UnreadBuffer()))
err = &jsontext.SyntacticError{ByteOffset: offset, Err: io.ErrUnexpectedEOF}
}
return transformSyntacticError(err)
}
if err := xd.CheckEOF(); err != nil {
return transformSyntacticError(err)
}
return nil
}
// A SyntaxError is a description of a JSON syntax error.
// [Unmarshal] will return a SyntaxError if the JSON can't be parsed.
type SyntaxError struct {
msg string // description of error
Offset int64 // error occurred after reading Offset bytes
}
func (e *SyntaxError) Error() string { return e.msg }
var errUnexpectedEnd = errors.New("unexpected end of JSON input")
func transformSyntacticError(err error) error {
switch serr, ok := err.(*jsontext.SyntacticError); {
case serr != nil:
// If the SyntacticError wraps an IO error, unwrap it
// to match v1 behavior which returned IO errors directly.
if export.IsIOError(serr.Err) {
return errors.Unwrap(serr.Err)
}
if serr.Err == io.ErrUnexpectedEOF {
serr.Err = errUnexpectedEnd
}
msg := serr.Err.Error()
if i := strings.Index(msg, " (expecting"); i >= 0 && !strings.Contains(msg, " in literal") {
msg = msg[:i]
}
return &SyntaxError{Offset: serr.ByteOffset, msg: syntaxErrorReplacer.Replace(msg)}
case ok:
return (*SyntaxError)(nil)
case export.IsIOError(err):
return errors.Unwrap(err) // v1 historically did not wrap IO errors
default:
return err
}
}
// syntaxErrorReplacer replaces certain string literals in the v2 error
// to better match the historical string rendering of syntax errors.
// In particular, v2 uses the terminology "object name" to match RFC 8259,
// while v1 uses "object key", which is not a term found in JSON literature.
var syntaxErrorReplacer = strings.NewReplacer(
"object name", "object key",
"at start of value", "looking for beginning of value",
"at start of string", "looking for beginning of object key string",
"after object value", "after object key:value pair",
"in number", "in numeric literal",
)
+311
View File
@@ -0,0 +1,311 @@
// 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
package json
import (
"bytes"
"errors"
"io"
"reflect"
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)
// A Decoder reads and decodes JSON values from an input stream.
type Decoder struct {
dec *jsontext.Decoder
opts jsonv2.Options
err error
// hadPeeked reports whether [Decoder.More] was called.
// It is reset by [Decoder.Decode] and [Decoder.Token].
hadPeeked bool
// hadEOF reports whether [Decoder.Token] had hit [io.EOF].
// It is reset by [Decoder.Decode] and [Decoder.Token].
hadEOF bool
}
// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
// Hide bytes.Buffer from jsontext since it implements optimizations that
// also limit certain ways it could be used. For example, one cannot write
// to the bytes.Buffer while it is in use by jsontext.Decoder.
if _, ok := r.(*bytes.Buffer); ok {
r = struct{ io.Reader }{r}
}
dec := new(Decoder)
dec.opts = DefaultOptionsV1()
dec.dec = jsontext.NewDecoder(r, dec.opts)
return dec
}
// UseNumber causes the Decoder to unmarshal a number into an
// interface value as a [Number] instead of as a float64.
func (dec *Decoder) UseNumber() {
if useNumber, _ := jsonv2.GetOption(dec.opts, unmarshalAnyWithRawNumber); !useNumber {
dec.opts = jsonv2.JoinOptions(dec.opts, unmarshalAnyWithRawNumber(true))
}
}
// DisallowUnknownFields causes the Decoder to return an error when the destination
// is a struct and the input contains object keys which do not match any
// non-ignored, exported fields in the destination.
func (dec *Decoder) DisallowUnknownFields() {
if reject, _ := jsonv2.GetOption(dec.opts, jsonv2.RejectUnknownMembers); !reject {
dec.opts = jsonv2.JoinOptions(dec.opts, jsonv2.RejectUnknownMembers(true))
}
}
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//
// See the documentation for [Unmarshal] for details about
// the conversion of JSON into a Go value.
func (dec *Decoder) Decode(v any) error {
if dec.err != nil {
return dec.err
}
b, err := dec.dec.ReadValue()
if err != nil {
dec.err = transformSyntacticError(err)
if dec.err.Error() == errUnexpectedEnd.Error() {
// NOTE: Decode has always been inconsistent with Unmarshal
// with regard to the exact error value for truncated input.
dec.err = io.ErrUnexpectedEOF
}
return dec.err
}
dec.hadPeeked = false
dec.hadEOF = false
return jsonv2.Unmarshal(b, v, dec.opts)
}
// Buffered returns a reader of the data remaining in the unread buffer,
// which may contain zero or more bytes.
// This is the data already consumed from the input [io.Reader],
// but not yet read by a [Decoder.Decode] or [Decoder.Token] call.
// It may contain bytes that do not form valid JSON as it has not yet
// been validated according to the JSON grammar.
// The exact amount of buffered data is an implementation detail
// of the Decoder and may change over time.
//
// It is the caller's responsibility to concatenate this buffer with
// the remainder of the input Reader to obtain the full sequence
// of bytes after the last decoded JSON value.
//
// The reader is valid until the next call to [Decoder.Decode] or [Decoder.Token].
func (dec *Decoder) Buffered() io.Reader {
return bytes.NewReader(dec.dec.UnreadBuffer())
}
// An Encoder writes JSON values to an output stream.
type Encoder struct {
w io.Writer
opts jsonv2.Options
err error
indentBuf bytes.Buffer
indentPrefix string
indentValue string
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
enc := new(Encoder)
enc.w = w
enc.opts = DefaultOptionsV1()
return enc
}
// Encode writes the JSON encoding of v to the stream,
// followed by a newline character.
//
// See the documentation for [Marshal] for details about the
// conversion of Go values to JSON.
func (enc *Encoder) Encode(v any) error {
if enc.err != nil {
return enc.err
}
e := export.GetBufferedEncoder(enc.opts)
defer export.PutBufferedEncoder(e)
if err := jsonv2.MarshalEncode(e, v); err != nil {
return err
}
b := export.Encoder(e).Buf // b must not leak current scope
if len(enc.indentPrefix)+len(enc.indentValue) > 0 {
enc.indentBuf.Reset()
if err := Indent(&enc.indentBuf, b, enc.indentPrefix, enc.indentValue); err != nil {
return err
}
b = enc.indentBuf.Bytes()
}
b = append(b, '\n')
if _, err := enc.w.Write(b); err != nil {
enc.err = err
return err
}
return nil
}
// SetIndent instructs the encoder to format each subsequent encoded
// value as if indented by the package-level function Indent(dst, src, prefix, indent).
// Calling SetIndent("", "") disables indentation.
func (enc *Encoder) SetIndent(prefix, indent string) {
// NOTE: Do not rely on the newer [jsontext.WithIndent] option since
// the v1 [Indent] behavior has historical bugs that cannot be changed
// for backward compatibility reasons.
enc.indentPrefix = prefix
enc.indentValue = indent
}
// SetEscapeHTML specifies whether problematic HTML characters
// should be escaped inside JSON quoted strings.
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
// to avoid certain safety problems that can arise when embedding JSON in HTML.
//
// In non-HTML settings where the escaping interferes with the readability
// of the output, SetEscapeHTML(false) disables this behavior.
func (enc *Encoder) SetEscapeHTML(on bool) {
if escape, _ := jsonv2.GetOption(enc.opts, jsontext.EscapeForHTML); escape != on {
enc.opts = jsonv2.JoinOptions(enc.opts, jsontext.EscapeForHTML(on))
}
}
// RawMessage is a raw encoded JSON value.
// It implements [Marshaler] and [Unmarshaler] and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage = jsontext.Value
// A Token holds a value of one of these types:
//
// - [Delim], for the four JSON delimiters [ ] { }
// - bool, for JSON booleans
// - float64, for JSON numbers
// - [Number], for JSON numbers
// - string, for JSON string literals
// - nil, for JSON null
type Token any
// A Delim is a JSON array or object delimiter, one of [ ] { or }.
type Delim rune
func (d Delim) String() string {
return string(d)
}
// Token returns the next JSON token in the input stream.
// At the end of the input stream, Token returns nil, [io.EOF].
//
// Token guarantees that the delimiters [ ] { } it returns are
// properly nested and matched: if Token encounters an unexpected
// delimiter in the input, it will return an error.
//
// The input stream consists of basic JSON values—bool, string,
// number, and null—along with delimiters [ ] { } of type [Delim]
// to mark the start and end of arrays and objects.
// Commas and colons are elided.
func (dec *Decoder) Token() (Token, error) {
if dec.err != nil {
return nil, dec.err
}
tok, err := dec.dec.ReadToken()
if err != nil {
// Historically, v1 would report just [io.EOF] if
// the stream is a prefix of a valid JSON value.
// It reports an unwrapped [io.ErrUnexpectedEOF] if
// truncated within a JSON token such as a literal, number, or string.
if errors.Is(err, io.ErrUnexpectedEOF) {
if len(bytes.Trim(dec.dec.UnreadBuffer(), " \r\n\t,:")) == 0 {
dec.hadEOF = true
return nil, io.EOF
}
return nil, io.ErrUnexpectedEOF
}
return nil, transformSyntacticError(err)
}
dec.hadPeeked = false
dec.hadEOF = false
switch k := tok.Kind(); k {
case 'n':
return nil, nil
case 'f':
return false, nil
case 't':
return true, nil
case '"':
return tok.String(), nil
case '0':
if useNumber, _ := jsonv2.GetOption(dec.opts, unmarshalAnyWithRawNumber); useNumber {
return Number(tok.String()), nil
}
v, err := tok.Float()
if err != nil {
return nil, &UnmarshalTypeError{Value: "number " + tok.String(), Type: reflect.TypeFor[float64](), Offset: dec.InputOffset()}
}
return v, nil
case '{', '}', '[', ']':
return Delim(k), nil
default:
panic("unreachable")
}
}
// More reports whether there is another element in the
// current array or object being parsed.
func (dec *Decoder) More() bool {
dec.hadPeeked = true
k := dec.dec.PeekKind()
if k == 0 {
if dec.err == nil {
// PeekKind doesn't distinguish between EOF and error,
// so read the next token to see which we get.
_, err := dec.dec.ReadToken()
if err == nil {
// This is only possible if jsontext violates its documentation.
err = errors.New("json: successful read after failed peek")
}
dec.err = transformSyntacticError(err)
}
return dec.err != io.EOF
}
return k != ']' && k != '}'
}
// InputOffset returns the input stream byte offset of the current decoder position.
// The offset gives the location of the end of the most recently returned token
// and the beginning of the next token.
func (dec *Decoder) InputOffset() int64 {
offset := dec.dec.InputOffset()
if dec.hadPeeked || dec.hadEOF {
// Historically, InputOffset reported the location of
// the end of the most recently returned token
// unless [Decoder.More] is called, in which case, it reported
// the beginning of the next token.
unreadBuffer := dec.dec.UnreadBuffer()
trailingTokens := bytes.TrimLeft(unreadBuffer, " \n\r\t")
if len(trailingTokens) > 0 {
leadingWhitespace := len(unreadBuffer) - len(trailingTokens)
offset += int64(leadingWhitespace)
// If [Decoder.Token] had encountered [io.EOF],
// then it also includes the upcoming comma or colon.
if dec.hadEOF && (trailingTokens[0] == ',' || trailingTokens[0] == ':') {
offset++
}
}
}
return offset
}