JSONCodable
JSONCodable copied to clipboard
Improve error messages
Preface: I've looked at a whole bunch of different JSON libraries for a talk I gave last week on JSON parsing in Swift. The talk was partly about different libraries, and partly about the JsonGen code generator I've build. Although I didn't mention JSONCodable in the talk, it is in my opinion one of the best JSON libraries I've looked at.
One of the very few features I miss in JSONCodable is verbose error messages. In JsonGen I try to include as much information as possible about errors in the JSON. So it's easier to debug problems in JSON coming from the server.
These two features in particular:
- Include the complete "path" of the error.
- Collect all errors, instead of stoping at the first error.
From my post on error messages, these are the types of error messages JsonGen generates:
2 errors in Blog struct
- subTitle: Value is not of expected type String?: `42`
▿ posts: 2 errors in array
▿ [1] 1 error in Post struct
- title: Field missing
▿ [2] 3 errors in Post struct
- title: Value is not of expected type String: `1`
- body: Value is not of expected type String: `42`
▿ author: 2 errors in Author struct
- name: Field missing
- email: Field missing
Feel free to close this issue if this isn't something you're interested in for JSONCodable. It's just something I wanted to bring to your attention.
For reference; I plan on separating the "library" part from the "code generator" part of JsonGen. So that users can use the library without the code generator. Or maybe change the code generator to generate decoders for other libraries.
JSONCodable is very interesting to me, if it includes all the features that are currently in the JsonGen library, I could even consider dropping my own library en using only JSONCodable as a target library for the code generator.
Perhaps of interest, my JsonDecodeError
enum is very similar to your JSONDecodableError
.
Providing more verbose error messages is definitely a interesting goal. I could see why there might be some issues implementing this though. Any ideas?
I think this can be implemented by storing a dictionary of errors in the JSONDecoder class:
var errors: [String: JSONDecodableError] = [:]
The decode functions can then be changed to store JSONDecodableError
and return optionals.
The new decodable init can be something like:
extension Company: JSONDecodable {
init(object: JSONObject) throws {
let decoder = JSONDecoder(object: object)
guard let
name: String = try decoder.decode("name"),
address: String? = try decoder.decode("address")
else { throw JSONDecodableError.MultipleErrors(decoder.errors) }
self.name = name
self.address = address
}
}
I haven't implemented this. So it isn't a complete design, but I think it's roughly something in this direction.
Just throwing this out there but it would be pretty cool if to indicate where in the object graph the error occurred. Maybe like:
JSONError([
[index: 0, name: JSONDecodableError.IncompatibleType(type: Int, expected: String)],
[index: 10, asset: [url: JSONDecodableError.IncompatibleType(type: Int, expected: String)]]
])
I realised this when implementing this in JsonGen:
The guard let
is shortcutting. So all decoders have to be called separately, to be able to collect all errors:
extension Company: JSONDecodable {
init(object: JSONObject) throws {
let decoder = JSONDecoder(object: object)
let _name: String = try decoder.decode("name")
let _address: String? = try decoder.decode("address")
guard let name = _name, address = _address else {
throw JSONDecodableError.MultipleErrors(decoder.errors)
}
self.name = name
self.address = address
}
}
The location in the object graph can be found by recursively following the errors in MultipleErrors.