swift-foundation
swift-foundation copied to clipboard
JSONEncoder can't encode what JSONDecoder decoded (they disagree on limits...)
Consider JSONs like
{
"nestedArray": [
{
"nestedArray": [
{
"nestedArray": [
{
"nestedArray": [
{
"nestedArray": [
{
"nestedArray": []
}
]
}
]
}
]
}
]
}
]
}
but deeper (for example bug1.json which has 255 levels). Now consider this program which decodes a thing and then reencodes it:
import Foundation
struct Thing: Codable {
var nestedArray: [Thing]?
}
do {
let data = try Data(contentsOf: URL(fileURLWithPath: CommandLine.arguments.dropFirst().first ?? "/tmp/file.json"))
let decoded = try JSONDecoder().decode(Thing.self, from: data)
do {
let encoded = try JSONEncoder().encode(decoded)
print("OK", encoded)
} catch {
print("encode ERROR", error)
}
} catch {
print("decode (or earlier) ERROR:", error)
}
You'd expect that JSONEncoder can encode everything that JSONDecoder decoded. But alas, here's what we see:
$ swiftc -O test.swift && ./test bug1.json
encode ERROR invalidValue(test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([test.Thing(nestedArray: Optional([]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))]))])), Swift.EncodingError.Context(codingPath: [], debugDescription: "Unable to encode the given top-level value to JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Too many nested arrays or dictionaries." UserInfo={NSDebugDescription=Too many nested arrays or dictionaries.})))
👀 The max number of depth is hardcoded as 512 for both decoding and encoding.
💭 Not yet tested, but my guess is that the value of depth for decoding starts with 0 but the one for encoding starts with 1, because JSONScanner increments depth after checking it, on the other hand, JSONWriter increments depth before checking it.
https://github.com/swiftlang/swift-foundation/blob/8c87e9eaacef5187a9e1a9f732348c274522085c/Sources/FoundationEssentials/JSON/JSONWriter.swift#L31-L50
@YOCKOW Does this need a fix? If so, can I look into opening a PR to address this?
@YOCKOW Does this need a fix? If so, can I look into opening a PR to address this?
Sure, you can. I'm assigning you to this issue, thank you.
Re-opening this issue as the original change was reverted due to https://github.com/swiftlang/swift-foundation/issues/1304