but found a number instead.", underlyingError: nil)),ios,swift"/>

首页 > 解决方案 > Expected to decode Dictionary but found a number instead.", underlyingError: nil))

问题描述

Working with the IGDB api, and am running into some issues.
https://api-docs.igdb.com/#game

I am calling on games, and using their sort functionality to present lists of games by their platform ID. My issue is, this works for some platforms, while others it does not and instead i get an error, so I have a bug somewhere. Ive looked up and down my structs and they all look correct, and I've even compared them to other wrapper projects in Swift for IGDB and they look to match up there too, but alas im getting the following error:

typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 44", intValue: 44), CodingKeys(stringValue: "cover", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a number instead.", underlyingError: nil))

Any ideas as to what I may be doing wrong?

Ive posted the entirety of the JSON response its returning here: https://pastebin.com/Y2kL5By3

My downloadJSON fuction looks like this:

func downloadJSON(platformSelected: String, completed: @escaping () -> () ) {
        let fields = "cover.image_id,name,summary,involved_companies.company.name,total_rating,platforms.category,platforms,popularity,platforms.versions.platform_logo.image_id,platforms.platform_logo.image_id,platforms.platform_logo.url"
        let gameCategory = 1
        let limit =  500
        let offset = 0
        let sortField = "name"
        let parameters = "fields \(fields);\nlimit \(limit);\noffset \(offset);\nwhere platforms = \(platformSelected);\nsort \(sortField):asc;\n"
        let postData = parameters.data(using: .utf8)
        print(platformSelected)
        let url = URL(string: "https://api-v3.igdb.com/games/")!
        let apiKey = "My API Key"
        var requestHeader = URLRequest.init(url: url )
        requestHeader.httpBody = postData
        requestHeader.httpMethod = "POST"
        requestHeader.setValue(apiKey, forHTTPHeaderField: "user-key")
        requestHeader.setValue("application/json", forHTTPHeaderField: "Accept")    
        URLSession.shared.dataTask(with: requestHeader) { (data, response, error) in

        if error == nil {
            do {
                let json = String(data: data!, encoding: .utf8)
                print("\(json)")
                self.games = try JSONDecoder().decode([Game].self, from: data!)



                DispatchQueue.main.async {
                    completed()
                }
            } catch {

                print(error)

            }
        }
    }.resume()

}

and my structs are here:


struct Game:Decodable {


    var name : String?
    var summary: String?
    var cover: Cover?
    var involvedCompanies: [InvolvedCompanies]?
    var totalRating: Double?
    var platforms: [Platform]?

    enum CodingKeys: String, CodingKey {
        case name, summary, cover
        case involvedCompanies = "involved_companies"
        case totalRating = "total_rating"
        case platforms
    }
}

struct Cover:Decodable {

    var id : UInt64
    var imageID : String?

    enum CodingKeys: String, CodingKey  {
        case id
        case imageID = "image_id"
    }

}

struct InvolvedCompanies:Decodable {


    var company: Companies?

}

struct Companies:Decodable {

    var name: String?

}






struct Platform: Decodable {
    let id: UInt64
    let name: String?
    let platformLogo: PlatformLogo?
    let versions: [PlatformVersion]?


    enum CodingKeys: String, CodingKey {
        case id, name
        case platformLogo = "platform_logo"
        case versions
    }
}

struct PlatformVersion: Decodable {

    let platformLogo : PlatformLogo?

    enum CodingKeys: String, CodingKey {

        case platformLogo = "platform_logo"
    }
}

struct PlatformLogo: Decodable {

    let imageID: String?
    let url: String?

    enum CodingKeys: String, CodingKey {
        case imageID = "image_id"
        case url
    }

}

标签: iosswift

解决方案


Let's understand the error first:

typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 44", intValue: 44), CodingKeys(stringValue: "cover", intValue: nil)], debugDescription: "Expected to decode Dictionary but found a number instead.", underlyingError: nil))

What is it saying? At index 44 of your array of Games, for the key cover, it was expecting a Dictionary (with is once translated into Codable, means a "Codable compliant struct"), but found a Int instead.

One way to manage that is to use a custom init(from decoder:) to handle the cases:

extension Game {

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decodeIfPresent(String.self, forKey: .name)
        summary = try container.decodeIfPresent(String.self, forKey: .summary)
        if let coverAttempt = try? container.decodeIfPresent(Cover.self, forKey: .cover) {
            cover = coverAttempt
        } else if let coverId = try? container.decodeIfPresent(UInt64.self, forKey: .cover) {
            cover = Cover(id: coverId, imageID: nil)
        } else {
            cover = nil
        }
        involvedCompanies = try container.decodeIfPresent([InvolvedCompanies].self, forKey: .involvedCompanies)
        totalRating = try container.decodeIfPresent(Double.self, forKey: .totalRating)
        platforms = try container.decodeIfPresent([Platform].self, forKey: .involvedCompanies)
    }
}

You can modify it since I assumed that when it's a number, it's the id of the cover, so when that's the case, I set a Cover with that id, but, no imageID. If you want it to be invalid when that's the case, just remove that test.


推荐阅读