302 lines
11 KiB
Go
302 lines
11 KiB
Go
// 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.
|
|
|
|
package jsontext
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/go-json-experiment/json/internal/jsonflags"
|
|
"github.com/go-json-experiment/json/internal/jsonopts"
|
|
"github.com/go-json-experiment/json/internal/jsonwire"
|
|
)
|
|
|
|
// Options configures [NewEncoder], [Encoder.Reset], [NewDecoder],
|
|
// and [Decoder.Reset] with specific features.
|
|
// Each function takes in a variadic list of options, where properties
|
|
// set in latter options override the value of previously set properties.
|
|
//
|
|
// There is a single Options type, which is used with both encoding and decoding.
|
|
// Some options affect both operations, while others only affect one operation:
|
|
//
|
|
// - [AllowDuplicateNames] affects encoding and decoding
|
|
// - [AllowInvalidUTF8] affects encoding and decoding
|
|
// - [EscapeForHTML] affects encoding only
|
|
// - [EscapeForJS] affects encoding only
|
|
// - [PreserveRawStrings] affects encoding only
|
|
// - [CanonicalizeRawInts] affects encoding only
|
|
// - [CanonicalizeRawFloats] affects encoding only
|
|
// - [ReorderRawObjects] affects encoding only
|
|
// - [SpaceAfterColon] affects encoding only
|
|
// - [SpaceAfterComma] affects encoding only
|
|
// - [Multiline] affects encoding only
|
|
// - [WithIndent] affects encoding only
|
|
// - [WithIndentPrefix] affects encoding only
|
|
//
|
|
// Options that do not affect a particular operation are ignored.
|
|
//
|
|
// The Options type is identical to [encoding/json.Options] and
|
|
// [encoding/json/v2.Options]. Options from the other packages may
|
|
// be passed to functionality in this package, but are ignored.
|
|
// Options from this package may be used with the other packages.
|
|
type Options = jsonopts.Options
|
|
|
|
// AllowDuplicateNames specifies that JSON objects may contain
|
|
// duplicate member names. Disabling the duplicate name check may provide
|
|
// performance benefits, but breaks compliance with RFC 7493, section 2.3.
|
|
// The input or output will still be compliant with RFC 8259,
|
|
// which leaves the handling of duplicate names as unspecified behavior.
|
|
//
|
|
// This affects either encoding or decoding.
|
|
func AllowDuplicateNames(v bool) Options {
|
|
if v {
|
|
return jsonflags.AllowDuplicateNames | 1
|
|
} else {
|
|
return jsonflags.AllowDuplicateNames | 0
|
|
}
|
|
}
|
|
|
|
// AllowInvalidUTF8 specifies that JSON strings may contain invalid UTF-8,
|
|
// which will be mangled as the Unicode replacement character, U+FFFD.
|
|
// This causes the encoder or decoder to break compliance with
|
|
// RFC 7493, section 2.1, and RFC 8259, section 8.1.
|
|
//
|
|
// This affects either encoding or decoding.
|
|
func AllowInvalidUTF8(v bool) Options {
|
|
if v {
|
|
return jsonflags.AllowInvalidUTF8 | 1
|
|
} else {
|
|
return jsonflags.AllowInvalidUTF8 | 0
|
|
}
|
|
}
|
|
|
|
// EscapeForHTML specifies that '<', '>', and '&' characters within JSON strings
|
|
// should be escaped as a hexadecimal Unicode codepoint (e.g., \u003c) so that
|
|
// the output is safe to embed within HTML.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func EscapeForHTML(v bool) Options {
|
|
if v {
|
|
return jsonflags.EscapeForHTML | 1
|
|
} else {
|
|
return jsonflags.EscapeForHTML | 0
|
|
}
|
|
}
|
|
|
|
// EscapeForJS specifies that U+2028 and U+2029 characters within JSON strings
|
|
// should be escaped as a hexadecimal Unicode codepoint (e.g., \u2028) so that
|
|
// the output is valid to embed within JavaScript. See RFC 8259, section 12.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func EscapeForJS(v bool) Options {
|
|
if v {
|
|
return jsonflags.EscapeForJS | 1
|
|
} else {
|
|
return jsonflags.EscapeForJS | 0
|
|
}
|
|
}
|
|
|
|
// PreserveRawStrings specifies that when encoding a raw JSON string in a
|
|
// [Token] or [Value], pre-escaped sequences
|
|
// in a JSON string are preserved to the output.
|
|
// However, raw strings still respect [EscapeForHTML] and [EscapeForJS]
|
|
// such that the relevant characters are escaped.
|
|
// If [AllowInvalidUTF8] is enabled, bytes of invalid UTF-8
|
|
// are preserved to the output.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func PreserveRawStrings(v bool) Options {
|
|
if v {
|
|
return jsonflags.PreserveRawStrings | 1
|
|
} else {
|
|
return jsonflags.PreserveRawStrings | 0
|
|
}
|
|
}
|
|
|
|
// CanonicalizeRawInts specifies that when encoding a raw JSON
|
|
// integer number (i.e., a number without a fraction and exponent) in a
|
|
// [Token] or [Value], the number is canonicalized
|
|
// according to RFC 8785, section 3.2.2.3. As a special case,
|
|
// the number -0 is canonicalized as 0.
|
|
//
|
|
// JSON numbers are treated as IEEE 754 double precision numbers.
|
|
// Any numbers with precision beyond what is representable by that form
|
|
// will lose their precision when canonicalized. For example,
|
|
// integer values beyond ±2⁵³ will lose their precision.
|
|
// For example, 1234567890123456789 is formatted as 1234567890123456800.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func CanonicalizeRawInts(v bool) Options {
|
|
if v {
|
|
return jsonflags.CanonicalizeRawInts | 1
|
|
} else {
|
|
return jsonflags.CanonicalizeRawInts | 0
|
|
}
|
|
}
|
|
|
|
// CanonicalizeRawFloats specifies that when encoding a raw JSON
|
|
// floating-point number (i.e., a number with a fraction or exponent) in a
|
|
// [Token] or [Value], the number is canonicalized
|
|
// according to RFC 8785, section 3.2.2.3. As a special case,
|
|
// the number -0 is canonicalized as 0.
|
|
//
|
|
// JSON numbers are treated as IEEE 754 double precision numbers.
|
|
// It is safe to canonicalize a serialized single precision number and
|
|
// parse it back as a single precision number and expect the same value.
|
|
// If a number exceeds ±1.7976931348623157e+308, which is the maximum
|
|
// finite number, then it saturated at that value and formatted as such.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func CanonicalizeRawFloats(v bool) Options {
|
|
if v {
|
|
return jsonflags.CanonicalizeRawFloats | 1
|
|
} else {
|
|
return jsonflags.CanonicalizeRawFloats | 0
|
|
}
|
|
}
|
|
|
|
// ReorderRawObjects specifies that when encoding a raw JSON object in a
|
|
// [Value], the object members are reordered according to
|
|
// RFC 8785, section 3.2.3.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func ReorderRawObjects(v bool) Options {
|
|
if v {
|
|
return jsonflags.ReorderRawObjects | 1
|
|
} else {
|
|
return jsonflags.ReorderRawObjects | 0
|
|
}
|
|
}
|
|
|
|
// SpaceAfterColon specifies that the JSON output should emit a space character
|
|
// after each colon separator following a JSON object name.
|
|
// If false, then no space character appears after the colon separator.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func SpaceAfterColon(v bool) Options {
|
|
if v {
|
|
return jsonflags.SpaceAfterColon | 1
|
|
} else {
|
|
return jsonflags.SpaceAfterColon | 0
|
|
}
|
|
}
|
|
|
|
// SpaceAfterComma specifies that the JSON output should emit a space character
|
|
// after each comma separator following a JSON object value or array element.
|
|
// If false, then no space character appears after the comma separator.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func SpaceAfterComma(v bool) Options {
|
|
if v {
|
|
return jsonflags.SpaceAfterComma | 1
|
|
} else {
|
|
return jsonflags.SpaceAfterComma | 0
|
|
}
|
|
}
|
|
|
|
// Multiline specifies that the JSON output should expand to multiple lines,
|
|
// where every JSON object member or JSON array element appears on
|
|
// a new, indented line according to the nesting depth.
|
|
//
|
|
// If [SpaceAfterColon] is not specified, then the default is true.
|
|
// If [SpaceAfterComma] is not specified, then the default is false.
|
|
// If [WithIndent] is not specified, then the default is "\t".
|
|
//
|
|
// If set to false, then the output is a single-line,
|
|
// where the only whitespace emitted is determined by the current
|
|
// values of [SpaceAfterColon] and [SpaceAfterComma].
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
func Multiline(v bool) Options {
|
|
if v {
|
|
return jsonflags.Multiline | 1
|
|
} else {
|
|
return jsonflags.Multiline | 0
|
|
}
|
|
}
|
|
|
|
// WithIndent specifies that the encoder should emit multiline output
|
|
// where each element in a JSON object or array begins on a new, indented line
|
|
// beginning with the indent prefix (see [WithIndentPrefix])
|
|
// followed by one or more copies of indent according to the nesting depth.
|
|
// The indent must only be composed of space or tab characters.
|
|
//
|
|
// If the intent to emit indented output without a preference for
|
|
// the particular indent string, then use [Multiline] instead.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
// Use of this option implies [Multiline] being set to true.
|
|
func WithIndent(indent string) Options {
|
|
// Fast-path: Return a constant for common indents, which avoids allocating.
|
|
// These are derived from analyzing the Go module proxy on 2023-07-01.
|
|
switch indent {
|
|
case "\t":
|
|
return jsonopts.Indent("\t") // ~14k usages
|
|
case " ":
|
|
return jsonopts.Indent(" ") // ~18k usages
|
|
case " ":
|
|
return jsonopts.Indent(" ") // ~1.7k usages
|
|
case " ":
|
|
return jsonopts.Indent(" ") // ~52k usages
|
|
case " ":
|
|
return jsonopts.Indent(" ") // ~12k usages
|
|
case "":
|
|
return jsonopts.Indent("") // ~1.5k usages
|
|
}
|
|
|
|
// Otherwise, allocate for this unique value.
|
|
if s := strings.Trim(indent, " \t"); len(s) > 0 {
|
|
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent")
|
|
}
|
|
return jsonopts.Indent(indent)
|
|
}
|
|
|
|
// WithIndentPrefix specifies that the encoder should emit multiline output
|
|
// where each element in a JSON object or array begins on a new, indented line
|
|
// beginning with the indent prefix followed by one or more copies of indent
|
|
// (see [WithIndent]) according to the nesting depth.
|
|
// The prefix must only be composed of space or tab characters.
|
|
//
|
|
// This only affects encoding and is ignored when decoding.
|
|
// Use of this option implies [Multiline] being set to true.
|
|
func WithIndentPrefix(prefix string) Options {
|
|
if s := strings.Trim(prefix, " \t"); len(s) > 0 {
|
|
panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent prefix")
|
|
}
|
|
return jsonopts.IndentPrefix(prefix)
|
|
}
|
|
|
|
/*
|
|
// TODO(https://go.dev/issue/56733): Implement WithByteLimit and WithDepthLimit.
|
|
|
|
// WithByteLimit sets a limit on the number of bytes of input or output bytes
|
|
// that may be consumed or produced for each top-level JSON value.
|
|
// If a [Decoder] or [Encoder] method call would need to consume/produce
|
|
// more than a total of n bytes to make progress on the top-level JSON value,
|
|
// then the call will report an error.
|
|
// Whitespace before and within the top-level value are counted against the limit.
|
|
// Whitespace after a top-level value are counted against the limit
|
|
// for the next top-level value.
|
|
//
|
|
// A non-positive limit is equivalent to no limit at all.
|
|
// If unspecified, the default limit is no limit at all.
|
|
// This affects either encoding or decoding.
|
|
func WithByteLimit(n int64) Options {
|
|
return jsonopts.ByteLimit(max(n, 0))
|
|
}
|
|
|
|
// WithDepthLimit sets a limit on the maximum depth of JSON nesting
|
|
// that may be consumed or produced for each top-level JSON value.
|
|
// If a [Decoder] or [Encoder] method call would need to consume or produce
|
|
// a depth greater than n to make progress on the top-level JSON value,
|
|
// then the call will report an error.
|
|
//
|
|
// A non-positive limit is equivalent to no limit at all.
|
|
// If unspecified, the default limit is 10000.
|
|
// This affects either encoding or decoding.
|
|
func WithDepthLimit(n int) Options {
|
|
return jsonopts.DepthLimit(max(n, 0))
|
|
}
|
|
*/
|