import Foundation
let json = """
{
"property": null
}
""".data(using: .utf8)!
struct Program<T: Decodable>: Decodable {
let property: T
static func decode() {
do {
try JSONDecoder().decode(Self.self, from: json)
} catch {
print("Error decoding \(T.self): \(error)\n")
}
}
}
Program<String>.decode()
Program<Int>.decode()
Program<[Double]>.decode()
Program<[String: Int]>.decode()
Program<Bool>.decode()
For every case, except for Bool
, we get valueNotFound
("Cannot get unkeyed decoding container -- found null value instead") error. Which is correct, according to the documentation.
Only for Bool
, for some reason, we get typeMismatch
("Expected to decode Bool but found null instead.") error.
On Linux (fiddle), [Double]
produces a valueNotFound
and everything else produces a typeMismatch
. This is consistent with the source of swift-core-libs-foundation, where unkeyedContainer
(which is called when decoding the array) throws valueNotFound
(source):
@usableFromInline func unkeyedContainer() throws -> UnkeyedDecodingContainer {
switch self.json {
case .array(let array):
...
case .null:
throw DecodingError.valueNotFound([String: JSONValue].self, DecodingError.Context(
codingPath: self.codingPath,
debugDescription: "Cannot get unkeyed decoding container -- found null value instead"
))
default:
...
}
}
whereas typeMismatch
is thrown by the single value container created for decoding the "scalar" types (source).
func decode(_: Bool.Type) throws -> Bool {
guard case .bool(let bool) = self.value else {
throw self.impl.createTypeMismatchError(type: Bool.self, value: self.value)
}
return bool
}
Because of this, I'd suspect JSONDecoder
on macOS is not purposefully implemented for Bool
to throw typeMismatch
and the others valueNotFound
. It seems to use the JSONSerialization
API to decode the JSON to an NSDictionary
first. The behaviour of Bool
vs the other types could be quirk of as a consequence of that.
I personally think the wording in the documentation for typeMismatch
and valueNotFound
are loose enough that it would be "correct" to throw either, when encountering a null value.