ios - 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
}
}
解决方案
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.
推荐阅读
- javascript - 如何以角度形式添加动态字段
- c++ - 在 lambda 函数声明中使用 auto
- postgresql - 如何在逻辑复制期间访问触发器函数内的表
- php - 使用 php 下拉菜单调用函数
- java - WireMock:200 个存根工作,但未找到具有服务故障的存根
- google-apps-script - 使用 GAS batchUpdate 将数据从 Google 表格自定义表单上的特定单元格复制到同一电子表格中不同工作表上的特定行
- python - 转置矩阵 Python Pandas
- c# - C#调用具有默认实现的接口方法
- r - 绘制多个组的嵌套分类值的折线图 (ggplot2)
- c# - 遍历列表,但无法访问不同的项目值