Is there a way to customize JSONEncoder formatting?

I want to customize the output of a JSONEncoder.

The prettyPrinted formatting option does what I want for containers with multiple values. That is, arrays with at least 2 elements, dictionaries with at least 2 keys, objects with at least 2 fields, and so forth.

But when an array, dictionary, or object has only one entry (or none), I want the encoded json to stay on a single line (provided the entry itself encodes to a single-line result).

For example:

// [Int]
[]      // Encode to []
[1]     // Encode to [1]
[1, 2]  // Encode to:
        // [
        //   1,
        //   2
        // ]

// [String: [Int]]
["a": []]      // Encode to { "a" : [] }
["a": [1]]     // Encode to { "a" : [1] }
["a": [1], "b": [2]]  // Encode to:
                      // {
                      //   "a" : [1],
                      //   "b" : [2]
                      // }
["a": [1, 2]]  // Encode to:
               // {
               //   "a" : [
               //     1,
               //     2
               //   ]
               // }

Is something like this possible?

What about taking it further by letting multi-element arrays encode to a single line (with a space after each comma), unless that would result in a length exceeding some threshold?

Basically, I’m looking for a “compactPrettyPrinted” format, which uses whitespace and newlines freely, but not excessively. Does such a thing exist, or is there a way to create one?

As you could see by checking the documentation, both answers are clearly no.

If I remember well, JSONEncoder is based on NSJSONSerialization, whose options are NSJSONWritingOptions | Apple Developer Documentation.

You may ask for an update to Foundation, or look for another JSON serialization utility, maybe.

1 Like

Indeed, JSONEncoder is currently backed by JSONSerialization, which doesn't offer this level of customizability (or a hook to provide it yourself). I also recommend filing some feedback with Apple, but in the meantime, it might be best to investigate a 3rd-party JSON-formatting library to pass the data through.

1 Like

Or you could "just" reformat the pretty printed output yourself, via eg an extension on String:

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let output = try! encoder.encode(value)
let jsonStr = String(data: output, encoding: .utf8)!
    .compactingPrettyPrintedJSON(maxLineLength: 80) // <-- implement something like this

Though it would certainly be nicer if JSONEncoder was more customizable.

2 Likes