swift - 使用 JSONDecoder 的数组与字典响应结构
问题描述
得到以下数据模型:
class ResponseMultipleElements<Element: Decodable>: Decodable {
let statuscode: Int
let response_type: Int
let errormessage: String?
let detailresponse: Element?
}
class Element<T: Decodable>: Decodable {
let count: String;
let element: T?
}
对于以下 API 响应结构:
{
"statuscode": 200,
"response_type": 3,
"errormessage": null,
"detailresponse": {
"count": "1",
"campaigns": [
{
"id": 1,
"name": "Foo",
"targetagegroup": null,
"creator":...
...
}
}
}
我触发 JSONDecoder 是这样的:
class APIService: NSObject {
func getCampaignList(completion: @escaping(Result<[Campaign], APIError>) -> Void) {
guard let endpoint = URL(string: apiBaseUrlSecure + "/campaignlist") else {fatalError()}
var request = URLRequest(url: endpoint)
request.addValue("Bearer " + UserDefaults.standard.string(forKey: "authtoken")!, forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let jsonData = data
else { print("ERROR: ", error ?? "unknown error"); completion(.failure(.responseError)); return }
do {
let response = try JSONDecoder().decode(ResponseMultipleElements<[Campaign]>.self, from: jsonData)
completion(.success(response.detailresponse!))
} catch {
print("Error is: ", error)
completion(.failure(.decodingError))
}
}
dataTask.resume()
}
...
}
我终于尝试像这样使用解码的活动对象
class CoopOverviewViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//do stuff
// load Campaigns
self.apiService.getCampaignList(completion: {result in
switch result {
case .success(let campaigns):
DispatchQueue.main.async {
print("CAMPAIGN DATA: ", campaigns[0].name)
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
...
}
现在我有两个问题:
1)
let element: T?
在此调用的 api 响应中实际上称为“活动”。但是,它可能是其他 api 响应中的合作、支付等,具有相同的 ResponseMultipleElements 周围结构。有没有办法在这里使键可交换,就像我使用泛型处理值一样?如果没有,我还能如何解决这个问题?
2)我收到此错误:
typeMismatch(Swift.Array<Any>,
Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "detailresponse", intValue: nil)],
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
我告诉 Swift,detailresponse 的“campaigns”部分是一个活动对象数组 - 至少这是我在查看 api 响应时的理解。但是,错误似乎说它是一本字典。首先,我不明白为什么会这样,并且真的很想理解它。其次,我不知道如何告诉它它应该期待一个字典而不是一个数组 - 这里有点与泛型混淆。
非常感谢您提前提供的帮助!
解决方案
这是一种添加自定义密钥解码策略以将任何内容映射CodingKey
到count
固定detailresponse
值的方法element
。
首先创建一个自定义CodingKey
struct AnyCodingKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) {
return nil
}
}
然后创建类似于 Sh_Khan 的答案的结构,在大多数情况下不需要类
struct ResponseMultipleElements<T: Decodable>: Decodable {
let statuscode : Int
let response_type : Int
let errormessage : String?
let detailresponse : Element<T>
}
struct Element<U: Decodable>: Decodable {
let count : String
let element : U
}
struct Campaign : Decodable {
let id : Int
let name : String
let targetagegroup : String?
}
现在是有趣的部分。创建一个自定义密钥解码策略,该策略始终为不返回element
的 CodingKey返回detailresponse
count
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom { codingKeys in
let lastKey = codingKeys.last!
if lastKey.intValue != nil || codingKeys.count != 2 { return lastKey }
if lastKey.stringValue == "count" { return lastKey }
return AnyCodingKey(stringValue: "element")!
}
let result = try decoder.decode(ResponseMultipleElements<[Campaign]>.self, from: data)
completion(.success(result.detailresponse.element))
} catch {
print("Error is: ", error)
completion(.failure(error))
}
推荐阅读
- scikit-learn - Scikit-Learn 中的成对操作和每对的不同过滤条件
- python - 如何从 OpenStreetMap 获取关系的几何图形?
- wordpress - WordPress 子页面不再显示
- php - 无法从 postgres 数据库中转义斜杠
- dynamics-crm - USD - Windows 导航规则未从仪表板托管控件触发
- c# - 仅比较 DateTime 中的日期部分是否知道 C# 中的时区偏移量?
- python - 如何使用映射而不是if-else来提高python程序的速度?
- nuxt.js - 如何在 nuxt.config.js 中使用 CopyWebpackPlugin?
- java - 如何在Java中对键上的地图列表进行排序?
- abap - 如何在 SAP ABAP 类中知道当前浏览器的 URL