Which JSONEncoder and JSONDecoder options matter beyond keys and dates?

2.5.3 · L2 · Learning

Predict first

What does this print?

let e = JSONEncoder()
e.outputFormatting = [.sortedKeys]
print(String(decoding: try e.encode(["a": "x/y"]), as: UTF8.self))
  • {"a":"x/y"}
  • {"a":"x\/y"}

Predict first

What does this print?

let d = JSONDecoder()
d.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "inf", negativeInfinity: "-inf", nan: "nan")
print((try? d.decode([Double].self, from: Data(#"["inf"]"#.utf8))) as Any)
  • nil
  • Optional([inf])

Mental model. These options are the settings on a printer: they do not change what you print, but they decide whether two prints of the same page are identical.

On the encoder, outputFormatting controls the text. .sortedKeys makes the output deterministic. Without it dictionary keys come out in a changing order, which breaks snapshot tests, cache keys and signatures. .prettyPrinted is for humans, and .withoutEscapingSlashes stops / becoming \/.

On the decoder, nonConformingFloatDecodingStrategy maps strings such as "inf" or "nan" to special floating-point values (JSON itself cannot represent them, so the default is to throw). dataDecodingStrategy chooses how a Data field is read, base64 by default. allowsJSON5 accepts comments and trailing commas. userInfo passes context into custom init(from:) code.

By default the encoder escapes the forward slash. Add .withoutEscapingSlashes to keep it as is.

The strategy tells the decoder to read the string "inf" as infinity. Without it the same input throws and the result is nil.

Common traps

  • Hashing or signing encoder output without .sortedKeys. Equal values produce different bytes between runs.
  • Sending Double.nan. The default encoder throws because JSON has no NaN.

They might follow up with

  • How do you send binary data in JSON? Base64 via Data; the default strategy does it for you.
  • Is pretty-printing free? No. Disable it for payloads you send over the network.

Related questions

  • 2.5.2 How are dates encoded and decoded in JSON, and why are they 31 years off?
  • 2.5.1 What does keyDecodingStrategy do, and when does convertFromSnakeCase not match?

← How are dates encoded and decoded in JSON, and why are they 31 years…   How do you read a DecodingError, and how do you decode tolerantly? →