数组与字典响应结构与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 的"campaign"部分是一个 campaign 对象的数组 - 至少这是我在查看 api 响应时的理解。但是,错误似乎说这是一本字典。首先,我不明白为什么会这样,真的很想理解它。其次,我不知道如何告诉它它应该期待字典而不是数组 - 在这里有点与泛型混淆。

非常感谢您提前提供的帮助!

这是一种添加自定义密钥解码策略的方法,以将detailresponsecount的任何CodingKey映射到固定值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中编码密钥的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))
}

相关内容

  • 没有找到相关文章

最新更新