使用 JSONDecoder 的数组与字典响应结构
Posted
技术标签:
【中文标题】使用 JSONDecoder 的数组与字典响应结构【英文标题】:Array vs Dictionary response structures with JSONDecoder 【发布时间】:2019-12-31 13:24:10 【问题描述】:得到以下数据模型:
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)")
)
...
现在我有 2 个问题:
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”部分是一个campaign 对象数组——至少我在查看 api 响应时是这样理解的。但是,错误似乎说它是一本字典。首先,我不明白为什么会这样,并且真的很想理解它。其次,我不知道如何告诉它它应该期待一个字典而不是一个数组 - 这里有点与泛型混淆。
非常感谢您提前提供的帮助!
【问题讨论】:
值response_type
是否与campaigns
、cooperations
等不同的键相关?如果是,则将response_type
声明为枚举并根据枚举对类型进行解码。如果有固定数量的不同类型,即使是泛型也是无关紧要的。
这行不通。我的问题是:根对象中response_type
中的3
是否代表campaigns
而其他值代表其他类型?
在我最初的问题中,在 Element 类中,我有一个带有名为“element”的键的属性。该键可以是活动或合作,也可以是 api 返回的任何对象数组,取决于调用的端点。这与 response_type 无论如何都不对应。我已经开始将该对象的类型设为通用,但不知道如何对密钥进行同样的操作。有解决办法吗?
请看我的回答。
【参考方案1】:
这是一种添加自定义密钥解码策略以将detailresponse
中的任何CodingKey
但count
映射到固定值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?
现在有趣的部分来了。创建一个自定义密钥解码策略,该策略始终为 detailresponse
中的 CodingKey 返回 element
,而不是 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))
【讨论】:
正是我想要的。像个迷人的人一样工作,并教给我有关自定义解码策略的课程。非常感谢!!!以上是关于使用 JSONDecoder 的数组与字典响应结构的主要内容,如果未能解决你的问题,请参考以下文章
JSONDecoder() 仅在 Swift 中处理 Null 值
如何使用 jsonDecoder 处理来自 JSON 响应的动态键?