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