如何准备 API 响应以在 swift 中与 jsonDecoder 一起使用
how prepare API response to use with jsonDecoder in swift
当我调用 API 并使用 Alamofire 从服务器获得响应时,我想使用 json
中的 "data" 对象
此数据来自API
{
"code": 200,
"hasError": false,
"data": [
{
"userSession": "43a1bd70-26bf-11e9-9ccd-00163eaf6bb4"
}
],
"message": "ok"
}
我想将 data
映射到我的 AuthModel
这是我的 AuthModel
:
struct AuthModel: Codable {
let userSession: String
enum CodingKeys: String, CodingKey {
case userSession = "userSession"
}
}
我对这行代码进行了编码,但它不起作用:
if let responseObject = response.result.value as? Dictionary<String,Any> {
if let hasError = responseObject["hasError"] as? Bool {
guard !hasError else { return }
do {
let decoder = JSONDecoder()
let authModel = try decoder.decode(AuthModel.self, from: responseObject["data"])
} catch {
print("Parse Error: ",error)
}
}
}
这不起作用,因为 responseObject["data"]
不是 NSData 类型
Cannot convert value of type '[String : Any]' to expected argument type 'Data'
获取 Data
响应而不是反序列化 Dictionary
例如
Alamofire.request(url).responseData { response in
并解码
let decoder = JSONDecoder()
let authModel = try decoder.decode(AuthModel.self, from: response.data!)
进入这些结构
struct AuthModel : Decodable {
let code : Int
let hasError : Bool
let message : String
let data : [Session]
}
struct Session : Decodable {
let userSession: String
}
已合成所有 CodingKey。
我认为您的 API 响应模式表明:
- 我们有什么问题(错误)吗?
- 我们有预期的数据吗?
基于这些,我们可以使用Enum
和Generics
。例如:
class ResponseObject<T: Codable>: Codable {
private var code : Int
private var hasError : Bool
private var message : String
private var data : T?
var result: Result {
guard !hasError else { return .error(code, message) }
guard let data = data else { return .error(0, "Data is not ready.") }
return .value(data)
}
enum Result {
case error(Int, String)
case value(T)
}
}
我们可以使用 ResponseObject
和我们预期的 data
:
let responseString = """
{
"code": 200,
"hasError": false,
"data": [
{
"userSession": "43a1bd70-26bf-11e9-9ccd-00163eaf6bb4"
}
],
"message": "ok"
}
"""
class AuthObject: Codable {
var userSession : String
}
if let jsonData = responseString.data(using: .utf8) {
do {
//ResponseObject<[AuthObject]> means: if we don't have error, the `data` object in response, will represent `[AuthObject]`.
let responseObject = try JSONDecoder().decode(ResponseObject<[AuthObject]>.self, from: jsonData)
//Using ResponseObject.Result Enum: We have error with related code and message, OR, we have our expected data.
switch responseObject.result {
case .error(let code, let message):
print("Error: \(code) - \(message)")
case .value(let authObjects):
print(authObjects.first!.userSession)
}
} catch {
print(error.localizedDescription)
}
}
当我调用 API 并使用 Alamofire 从服务器获得响应时,我想使用 json
中的 "data" 对象此数据来自API
{
"code": 200,
"hasError": false,
"data": [
{
"userSession": "43a1bd70-26bf-11e9-9ccd-00163eaf6bb4"
}
],
"message": "ok"
}
我想将 data
映射到我的 AuthModel
这是我的 AuthModel
:
struct AuthModel: Codable {
let userSession: String
enum CodingKeys: String, CodingKey {
case userSession = "userSession"
}
}
我对这行代码进行了编码,但它不起作用:
if let responseObject = response.result.value as? Dictionary<String,Any> {
if let hasError = responseObject["hasError"] as? Bool {
guard !hasError else { return }
do {
let decoder = JSONDecoder()
let authModel = try decoder.decode(AuthModel.self, from: responseObject["data"])
} catch {
print("Parse Error: ",error)
}
}
}
这不起作用,因为 responseObject["data"]
不是 NSData 类型
Cannot convert value of type '[String : Any]' to expected argument type 'Data'
获取 Data
响应而不是反序列化 Dictionary
例如
Alamofire.request(url).responseData { response in
并解码
let decoder = JSONDecoder()
let authModel = try decoder.decode(AuthModel.self, from: response.data!)
进入这些结构
struct AuthModel : Decodable {
let code : Int
let hasError : Bool
let message : String
let data : [Session]
}
struct Session : Decodable {
let userSession: String
}
已合成所有 CodingKey。
我认为您的 API 响应模式表明:
- 我们有什么问题(错误)吗?
- 我们有预期的数据吗?
基于这些,我们可以使用Enum
和Generics
。例如:
class ResponseObject<T: Codable>: Codable {
private var code : Int
private var hasError : Bool
private var message : String
private var data : T?
var result: Result {
guard !hasError else { return .error(code, message) }
guard let data = data else { return .error(0, "Data is not ready.") }
return .value(data)
}
enum Result {
case error(Int, String)
case value(T)
}
}
我们可以使用 ResponseObject
和我们预期的 data
:
let responseString = """
{
"code": 200,
"hasError": false,
"data": [
{
"userSession": "43a1bd70-26bf-11e9-9ccd-00163eaf6bb4"
}
],
"message": "ok"
}
"""
class AuthObject: Codable {
var userSession : String
}
if let jsonData = responseString.data(using: .utf8) {
do {
//ResponseObject<[AuthObject]> means: if we don't have error, the `data` object in response, will represent `[AuthObject]`.
let responseObject = try JSONDecoder().decode(ResponseObject<[AuthObject]>.self, from: jsonData)
//Using ResponseObject.Result Enum: We have error with related code and message, OR, we have our expected data.
switch responseObject.result {
case .error(let code, let message):
print("Error: \(code) - \(message)")
case .value(let authObjects):
print(authObjects.first!.userSession)
}
} catch {
print(error.localizedDescription)
}
}