Source file src/encoding/json/jsontext/options.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.jsonv2
     6  
     7  package jsontext
     8  
     9  import (
    10  	"strings"
    11  
    12  	"encoding/json/internal/jsonflags"
    13  	"encoding/json/internal/jsonopts"
    14  	"encoding/json/internal/jsonwire"
    15  )
    16  
    17  // Options configures [NewEncoder], [Encoder.Reset], [NewDecoder],
    18  // and [Decoder.Reset] with specific features.
    19  // Each function takes in a variadic list of options, where properties
    20  // set in latter options override the value of previously set properties.
    21  //
    22  // There is a single Options type, which is used with both encoding and decoding.
    23  // Some options affect both operations, while others only affect one operation:
    24  //
    25  //   - [AllowDuplicateNames] affects encoding and decoding
    26  //   - [AllowInvalidUTF8] affects encoding and decoding
    27  //   - [EscapeForHTML] affects encoding only
    28  //   - [EscapeForJS] affects encoding only
    29  //   - [PreserveRawStrings] affects encoding only
    30  //   - [CanonicalizeRawInts] affects encoding only
    31  //   - [CanonicalizeRawFloats] affects encoding only
    32  //   - [ReorderRawObjects] affects encoding only
    33  //   - [SpaceAfterColon] affects encoding only
    34  //   - [SpaceAfterComma] affects encoding only
    35  //   - [Multiline] affects encoding only
    36  //   - [WithIndent] affects encoding only
    37  //   - [WithIndentPrefix] affects encoding only
    38  //
    39  // Options that do not affect a particular operation are ignored.
    40  //
    41  // The Options type is identical to [encoding/json.Options] and
    42  // [encoding/json/v2.Options]. Options from the other packages may
    43  // be passed to functionality in this package, but are ignored.
    44  // Options from this package may be used with the other packages.
    45  type Options = jsonopts.Options
    46  
    47  // AllowDuplicateNames specifies that JSON objects may contain
    48  // duplicate member names. Disabling the duplicate name check may provide
    49  // performance benefits, but breaks compliance with RFC 7493, section 2.3.
    50  // The input or output will still be compliant with RFC 8259,
    51  // which leaves the handling of duplicate names as unspecified behavior.
    52  //
    53  // This affects either encoding or decoding.
    54  func AllowDuplicateNames(v bool) Options {
    55  	if v {
    56  		return jsonflags.AllowDuplicateNames | 1
    57  	} else {
    58  		return jsonflags.AllowDuplicateNames | 0
    59  	}
    60  }
    61  
    62  // AllowInvalidUTF8 specifies that JSON strings may contain invalid UTF-8,
    63  // which will be mangled as the Unicode replacement character, U+FFFD.
    64  // This causes the encoder or decoder to break compliance with
    65  // RFC 7493, section 2.1, and RFC 8259, section 8.1.
    66  //
    67  // This affects either encoding or decoding.
    68  func AllowInvalidUTF8(v bool) Options {
    69  	if v {
    70  		return jsonflags.AllowInvalidUTF8 | 1
    71  	} else {
    72  		return jsonflags.AllowInvalidUTF8 | 0
    73  	}
    74  }
    75  
    76  // EscapeForHTML specifies that '<', '>', and '&' characters within JSON strings
    77  // should be escaped as a hexadecimal Unicode codepoint (e.g., \u003c) so that
    78  // the output is safe to embed within HTML.
    79  //
    80  // This only affects encoding and is ignored when decoding.
    81  func EscapeForHTML(v bool) Options {
    82  	if v {
    83  		return jsonflags.EscapeForHTML | 1
    84  	} else {
    85  		return jsonflags.EscapeForHTML | 0
    86  	}
    87  }
    88  
    89  // EscapeForJS specifies that U+2028 and U+2029 characters within JSON strings
    90  // should be escaped as a hexadecimal Unicode codepoint (e.g., \u2028) so that
    91  // the output is valid to embed within JavaScript. See RFC 8259, section 12.
    92  //
    93  // This only affects encoding and is ignored when decoding.
    94  func EscapeForJS(v bool) Options {
    95  	if v {
    96  		return jsonflags.EscapeForJS | 1
    97  	} else {
    98  		return jsonflags.EscapeForJS | 0
    99  	}
   100  }
   101  
   102  // PreserveRawStrings specifies that when encoding a raw JSON string in a
   103  // [Token] or [Value], pre-escaped sequences
   104  // in a JSON string are preserved to the output.
   105  // However, raw strings still respect [EscapeForHTML] and [EscapeForJS]
   106  // such that the relevant characters are escaped.
   107  // If [AllowInvalidUTF8] is enabled, bytes of invalid UTF-8
   108  // are preserved to the output.
   109  //
   110  // This only affects encoding and is ignored when decoding.
   111  func PreserveRawStrings(v bool) Options {
   112  	if v {
   113  		return jsonflags.PreserveRawStrings | 1
   114  	} else {
   115  		return jsonflags.PreserveRawStrings | 0
   116  	}
   117  }
   118  
   119  // CanonicalizeRawInts specifies that when encoding a raw JSON
   120  // integer number (i.e., a number without a fraction and exponent) in a
   121  // [Token] or [Value], the number is canonicalized
   122  // according to RFC 8785, section 3.2.2.3. As a special case,
   123  // the number -0 is canonicalized as 0.
   124  //
   125  // JSON numbers are treated as IEEE 754 double precision numbers.
   126  // Any numbers with precision beyond what is representable by that form
   127  // will lose their precision when canonicalized. For example,
   128  // integer values beyond ±2⁵³ will lose their precision.
   129  // For example, 1234567890123456789 is formatted as 1234567890123456800.
   130  //
   131  // This only affects encoding and is ignored when decoding.
   132  func CanonicalizeRawInts(v bool) Options {
   133  	if v {
   134  		return jsonflags.CanonicalizeRawInts | 1
   135  	} else {
   136  		return jsonflags.CanonicalizeRawInts | 0
   137  	}
   138  }
   139  
   140  // CanonicalizeRawFloats specifies that when encoding a raw JSON
   141  // floating-point number (i.e., a number with a fraction or exponent) in a
   142  // [Token] or [Value], the number is canonicalized
   143  // according to RFC 8785, section 3.2.2.3. As a special case,
   144  // the number -0 is canonicalized as 0.
   145  //
   146  // JSON numbers are treated as IEEE 754 double precision numbers.
   147  // It is safe to canonicalize a serialized single precision number and
   148  // parse it back as a single precision number and expect the same value.
   149  // If a number exceeds ±1.7976931348623157e+308, which is the maximum
   150  // finite number, then it saturated at that value and formatted as such.
   151  //
   152  // This only affects encoding and is ignored when decoding.
   153  func CanonicalizeRawFloats(v bool) Options {
   154  	if v {
   155  		return jsonflags.CanonicalizeRawFloats | 1
   156  	} else {
   157  		return jsonflags.CanonicalizeRawFloats | 0
   158  	}
   159  }
   160  
   161  // ReorderRawObjects specifies that when encoding a raw JSON object in a
   162  // [Value], the object members are reordered according to
   163  // RFC 8785, section 3.2.3.
   164  //
   165  // This only affects encoding and is ignored when decoding.
   166  func ReorderRawObjects(v bool) Options {
   167  	if v {
   168  		return jsonflags.ReorderRawObjects | 1
   169  	} else {
   170  		return jsonflags.ReorderRawObjects | 0
   171  	}
   172  }
   173  
   174  // SpaceAfterColon specifies that the JSON output should emit a space character
   175  // after each colon separator following a JSON object name.
   176  // If false, then no space character appears after the colon separator.
   177  //
   178  // This only affects encoding and is ignored when decoding.
   179  func SpaceAfterColon(v bool) Options {
   180  	if v {
   181  		return jsonflags.SpaceAfterColon | 1
   182  	} else {
   183  		return jsonflags.SpaceAfterColon | 0
   184  	}
   185  }
   186  
   187  // SpaceAfterComma specifies that the JSON output should emit a space character
   188  // after each comma separator following a JSON object value or array element.
   189  // If false, then no space character appears after the comma separator.
   190  //
   191  // This only affects encoding and is ignored when decoding.
   192  func SpaceAfterComma(v bool) Options {
   193  	if v {
   194  		return jsonflags.SpaceAfterComma | 1
   195  	} else {
   196  		return jsonflags.SpaceAfterComma | 0
   197  	}
   198  }
   199  
   200  // Multiline specifies that the JSON output should expand to multiple lines,
   201  // where every JSON object member or JSON array element appears on
   202  // a new, indented line according to the nesting depth.
   203  //
   204  // If [SpaceAfterColon] is not specified, then the default is true.
   205  // If [SpaceAfterComma] is not specified, then the default is false.
   206  // If [WithIndent] is not specified, then the default is "\t".
   207  //
   208  // If set to false, then the output is a single-line,
   209  // where the only whitespace emitted is determined by the current
   210  // values of [SpaceAfterColon] and [SpaceAfterComma].
   211  //
   212  // This only affects encoding and is ignored when decoding.
   213  func Multiline(v bool) Options {
   214  	if v {
   215  		return jsonflags.Multiline | 1
   216  	} else {
   217  		return jsonflags.Multiline | 0
   218  	}
   219  }
   220  
   221  // WithIndent specifies that the encoder should emit multiline output
   222  // where each element in a JSON object or array begins on a new, indented line
   223  // beginning with the indent prefix (see [WithIndentPrefix])
   224  // followed by one or more copies of indent according to the nesting depth.
   225  // The indent must only be composed of space or tab characters.
   226  //
   227  // If the intent to emit indented output without a preference for
   228  // the particular indent string, then use [Multiline] instead.
   229  //
   230  // This only affects encoding and is ignored when decoding.
   231  // Use of this option implies [Multiline] being set to true.
   232  func WithIndent(indent string) Options {
   233  	// Fast-path: Return a constant for common indents, which avoids allocating.
   234  	// These are derived from analyzing the Go module proxy on 2023-07-01.
   235  	switch indent {
   236  	case "\t":
   237  		return jsonopts.Indent("\t") // ~14k usages
   238  	case "    ":
   239  		return jsonopts.Indent("    ") // ~18k usages
   240  	case "   ":
   241  		return jsonopts.Indent("   ") // ~1.7k usages
   242  	case "  ":
   243  		return jsonopts.Indent("  ") // ~52k usages
   244  	case " ":
   245  		return jsonopts.Indent(" ") // ~12k usages
   246  	case "":
   247  		return jsonopts.Indent("") // ~1.5k usages
   248  	}
   249  
   250  	// Otherwise, allocate for this unique value.
   251  	if s := strings.Trim(indent, " \t"); len(s) > 0 {
   252  		panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent")
   253  	}
   254  	return jsonopts.Indent(indent)
   255  }
   256  
   257  // WithIndentPrefix specifies that the encoder should emit multiline output
   258  // where each element in a JSON object or array begins on a new, indented line
   259  // beginning with the indent prefix followed by one or more copies of indent
   260  // (see [WithIndent]) according to the nesting depth.
   261  // The prefix must only be composed of space or tab characters.
   262  //
   263  // This only affects encoding and is ignored when decoding.
   264  // Use of this option implies [Multiline] being set to true.
   265  func WithIndentPrefix(prefix string) Options {
   266  	if s := strings.Trim(prefix, " \t"); len(s) > 0 {
   267  		panic("json: invalid character " + jsonwire.QuoteRune(s) + " in indent prefix")
   268  	}
   269  	return jsonopts.IndentPrefix(prefix)
   270  }
   271  
   272  /*
   273  // TODO(https://go.dev/issue/56733): Implement WithByteLimit and WithDepthLimit.
   274  
   275  // WithByteLimit sets a limit on the number of bytes of input or output bytes
   276  // that may be consumed or produced for each top-level JSON value.
   277  // If a [Decoder] or [Encoder] method call would need to consume/produce
   278  // more than a total of n bytes to make progress on the top-level JSON value,
   279  // then the call will report an error.
   280  // Whitespace before and within the top-level value are counted against the limit.
   281  // Whitespace after a top-level value are counted against the limit
   282  // for the next top-level value.
   283  //
   284  // A non-positive limit is equivalent to no limit at all.
   285  // If unspecified, the default limit is no limit at all.
   286  // This affects either encoding or decoding.
   287  func WithByteLimit(n int64) Options {
   288  	return jsonopts.ByteLimit(max(n, 0))
   289  }
   290  
   291  // WithDepthLimit sets a limit on the maximum depth of JSON nesting
   292  // that may be consumed or produced for each top-level JSON value.
   293  // If a [Decoder] or [Encoder] method call would need to consume or produce
   294  // a depth greater than n to make progress on the top-level JSON value,
   295  // then the call will report an error.
   296  //
   297  // A non-positive limit is equivalent to no limit at all.
   298  // If unspecified, the default limit is 10000.
   299  // This affects either encoding or decoding.
   300  func WithDepthLimit(n int) Options {
   301  	return jsonopts.DepthLimit(max(n, 0))
   302  }
   303  */
   304  

View as plain text