Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error messages #27

Open
tomlokhorst opened this issue Jan 22, 2016 · 5 comments
Open

Improve error messages #27

tomlokhorst opened this issue Jan 22, 2016 · 5 comments

Comments

@tomlokhorst
Copy link

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:

  1. Include the complete "path" of the error.
  2. 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.

@tomlokhorst
Copy link
Author

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.

@matthewcheok
Copy link
Owner

Providing more verbose error messages is definitely a interesting goal. I could see why there might be some issues implementing this though. Any ideas?

@tomlokhorst
Copy link
Author

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.

@matthewcheok
Copy link
Owner

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)]]
])

@tomlokhorst
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants