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.
2
Answers
On Linux (fiddle),
[Double]
produces avalueNotFound
and everything else produces atypeMismatch
. This is consistent with the source of swift-core-libs-foundation, whereunkeyedContainer
(which is called when decoding the array) throwsvalueNotFound
(source):whereas
typeMismatch
is thrown by the single value container created for decoding the "scalar" types (source).Because of this, I’d suspect
JSONDecoder
on macOS is not purposefully implemented forBool
to throwtypeMismatch
and the othersvalueNotFound
. It seems to use theJSONSerialization
API to decode the JSON to anNSDictionary
first. The behaviour ofBool
vs the other types could be quirk of as a consequence of that.I personally think the wording in the documentation for
typeMismatch
andvalueNotFound
are loose enough that it would be "correct" to throw either, when encountering a null value.When decoding JSON with Swift’s
JSONDecoder
, if a non-optional type likeString
,Int
,[Double]
, or[String: Int]
encounters a null value, it throws a valueNotFound error because it can’t decode null into a non-optional type.However, for
Bool
, which can only be true or false, encountering null throws a typeMismatch error because null isn’t considered a valid boolean value.To handle this, you can make your property type optional if you expect null values in your JSON.