Search code examples
swiftjsondecoder

JSONDecoder fails to parse a list of maps


I have prepared a simple test Playground at Github to demo my problem:

Xcode screenshot

My Swift code:

struct TopResponse: Codable {
    let results: [Top]
}
struct Top: Codable {
    let uid: Int
    let elo: Int
    let given: String
    let photo: String?
    let motto: String?
    let avg_score: String?
    let avg_time: String?
}

let url = URL(string: "https://slova.de/ws/top")!
let task = URLSession.shared.dataTask(with: url) {
    data, response, error in
    
    let decoder = JSONDecoder()
    guard let data2 = data,
          let tops = try? decoder.decode(TopResponse.self, from:
                                            data2) else { return }
    print(tops.results[4].given)
}
task.resume()

fails to parse the fetched JSON string and does not print anything.

What could be the problem here please?


Solution

  • What's wrong with your code?

    try?
    

    That's the main culprit. Why? You are ignoring the error thrown by the decode(_:from:). You are ignoring the error that could give you the exact reason or at least a hint on why it failed. Instead, write a proper do { try ... } catch { ... }.

    So:

    guard let data2 = data,
          let tops = try? decoder.decode(TopResponse.self, from:
                                            data2) else { return }
    print(tops.results[4].given)
    

    =>

    guard let data2 = data else { return }
    do {
    let tops = try decoder.decode(TopResponse.self, from: data2)
    print(tops.results[4].given)
    } catch {
        print("Got error while parsing: \(error)")
        print("With response: \(String(data: data2, encoding: .utf8))") //Just in case because I've seen plenty of code where expected JSON wasn't the one received: it was an error, doc changed, etc...
    }
    

    Output for the first print:

    $>Got error while parsing: keyNotFound(CodingKeys(stringValue: "results", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"results\", intValue: nil) (\"results\").", underlyingError: nil))
    

    Fix:

    struct TopResponse: Codable {
        let results: [Top]
    
        enum CodingKeys: String, CodingKey {
            case results = "data"
        }
    }
    

    Or rename results with data.

    Then, next error:

    $>Got error while parsing: typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "avg_score", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
    

    Extract from JSON:

        "avg_score": 20.4
    

    It's not a String (the value it's not between double quotes), that's a Double.

    Fix:

    let avg_score: String?
    

    =>

    let avg_score: Double?