在swift 4中解码动态JSON结构
我有以下问题,我不知道如何处理 我的在swift 4中解码动态JSON结构,swift,decodable,Swift,Decodable,我有以下问题,我不知道如何处理 我的JSON响应可以如下所示: { "data": { "id": 7, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo" }, "error": n
JSON
响应可以如下所示:
{
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}
{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}
或者像这样:
{
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}
{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}
因此,简而言之,数据可以是单个对象,也可以是阵列。我所拥有的是:
struct ApiData: Decodable {
var data: DataObject?
var error: String?
}
struct DataObject: Decodable {
var userId: Int?
enum CodingKeys: String, CodingKey {
case userId = "id"
}
}
这在第一个用例中可以很好地工作,但一旦数据转换为
var数据:[DataObject?]
如何在不复制代码的情况下使其动态化
编辑:这也是我解码对象的方式
func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(_ apiData: ApiData?) -> ()) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
session.dataTask(with: urlRequest) {
(data, response, error) in
guard let _ = response, let data = data else {return}
if let responseCode = response as? HTTPURLResponse {
print("Response has status code: \(responseCode.statusCode)")
}
do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
completion(retreived)
} catch let decodeError as NSError {
print("Decoder error: \(decodeError.localizedDescription)\n")
return
}
}.resume()
}
如果您的数据只有两种可能的结果,一种选择是尝试将数据解析为预期的类型之一,如果失败,您知道数据属于其他类型,然后可以相应地处理它
请参见如果您的数据只有两种可能的结果,一种选择是尝试将数据解析为预期的类型之一,如果失败,您知道数据属于其他类型,然后可以相应地处理它 请参见,您可以尝试
struct Root: Codable {
let data: DataUnion
let error: String?
}
enum DataUnion: Codable {
case dataClass(DataClass)
case datumArray([Datum])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode([Datum].self) {
self = .datumArray(x)
return
}
if let x = try? container.decode(DataClass.self) {
self = .dataClass(x)
return
}
throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .dataClass(let x):
try container.encode(x)
case .datumArray(let x):
try container.encode(x)
}
}
}
struct Datum: Codable {
let id: Int
}
struct DataClass: Codable {
let id: Int
let token: String
}
你可以试试
struct Root: Codable {
let data: DataUnion
let error: String?
}
enum DataUnion: Codable {
case dataClass(DataClass)
case datumArray([Datum])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode([Datum].self) {
self = .datumArray(x)
return
}
if let x = try? container.decode(DataClass.self) {
self = .dataClass(x)
return
}
throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .dataClass(let x):
try container.encode(x)
case .datumArray(let x):
try container.encode(x)
}
}
}
struct Datum: Codable {
let id: Int
}
struct DataClass: Codable {
let id: Int
let token: String
}
如果
数据
可以是单个对象或数组,请编写一个自定义初始值设定项,首先对数组进行解码,如果发生类型不匹配错误,请对单个对象进行解码<代码>数据仍然声明为数组
由于token
仅出现在单个对象中,因此属性声明为可选
struct ApiData: Decodable {
let data : [DataObject]
let error : String?
private enum CodingKeys : String, CodingKey { case data, error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
data = try container.decode([DataObject].self, forKey: .data)
} catch DecodingError.typeMismatch {
data = [try container.decode(DataObject.self, forKey: .data)]
}
error = try container.decodeIfPresent(String.self, forKey: .error)
}
}
struct DataObject: Decodable {
let userId : Int
let token : String?
private enum CodingKeys: String, CodingKey { case userId = "id", token }
}
编辑:可以改进接收数据的代码。您应该添加更好的错误处理,以返回所有可能的错误:
func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
session.dataTask(with: urlRequest) {
(data, response, error) in
if let error = error { completion(nil, error); return }
if let responseCode = response as? HTTPURLResponse {
print("Response has status code: \(responseCode.statusCode)")
}
do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
completion(retreived, nil)
} catch {
print("Decoder error: ", error)
completion(nil, error)
}
}.resume()
}
如果
数据
可以是单个对象或数组,请编写一个自定义初始值设定项,首先对数组进行解码,如果发生类型不匹配错误,请对单个对象进行解码<代码>数据仍然声明为数组
由于token
仅出现在单个对象中,因此属性声明为可选
struct ApiData: Decodable {
let data : [DataObject]
let error : String?
private enum CodingKeys : String, CodingKey { case data, error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
data = try container.decode([DataObject].self, forKey: .data)
} catch DecodingError.typeMismatch {
data = [try container.decode(DataObject.self, forKey: .data)]
}
error = try container.decodeIfPresent(String.self, forKey: .error)
}
}
struct DataObject: Decodable {
let userId : Int
let token : String?
private enum CodingKeys: String, CodingKey { case userId = "id", token }
}
编辑:可以改进接收数据的代码。您应该添加更好的错误处理,以返回所有可能的错误:
func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
session.dataTask(with: urlRequest) {
(data, response, error) in
if let error = error { completion(nil, error); return }
if let responseCode = response as? HTTPURLResponse {
print("Response has status code: \(responseCode.statusCode)")
}
do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
completion(retreived, nil)
} catch {
print("Decoder error: ", error)
completion(nil, error)
}
}.resume()
}
使用
generic
的强大功能,它简单如下:
struct ApiData<T: Decodable>: Decodable {
var data: T?
var error: String?
}
struct DataObject: Decodable {
private var id: Int?
var userId:Int? {
return id
}
}
struct ApiData:可解码{
var数据:T?
变量错误:字符串?
}
结构数据对象:可解码{
私有变量id:Int?
var userId:Int{
返回id
}
}
使用
if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
//Do somthing
} else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
// Do somthing
}
如果让obj=try?NetworkManager.shared.decoder.decode(ApiData.self,from:data){
//干坏事
}如果let array=try NetworkManager.shared.decoder.decode(ApiData.self,from:data),则为else{
//干坏事
}
使用通用的功能,简单如下:
struct ApiData<T: Decodable>: Decodable {
var data: T?
var error: String?
}
struct DataObject: Decodable {
private var id: Int?
var userId:Int? {
return id
}
}
struct ApiData:可解码{
var数据:T?
变量错误:字符串?
}
结构数据对象:可解码{
私有变量id:Int?
var userId:Int{
返回id
}
}
使用
if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
//Do somthing
} else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
// Do somthing
}
如果让obj=try?NetworkManager.shared.decoder.decode(ApiData.self,from:data){
//干坏事
}如果let array=try NetworkManager.shared.decoder.decode(ApiData.self,from:data),则为else{
//干坏事
}
如果出现错误,API会发送什么!=null
?@vadian它发送一个字符串,你可以在ApiData结构中看到它。@RobertDresler我不确定我是否理解这个问题?借助于符合可解码protocol@RobertDresler我编辑了这篇文章来回答你的问题。使用genericAnd的功能,如果出现错误,API会发送什么消息!=null
?@vadian它发送一个字符串,你可以在ApiData结构中看到它。@RobertDresler我不确定我是否理解这个问题?借助于符合可解码protocol@RobertDresler我编辑了这篇文章来回答你的问题。使用genericYes的强大功能,但这会复制代码,两种类型都有var userId:Int?还有一堆我在上面的例子中没有粘贴的其他用户信息。是的,但这会重复代码,两种类型都有var userId:Int?还有一堆我在上面的例子中没有粘贴的其他用户信息。谢谢,但这并不能避免重复。structs Datum和DataClass都有let id。您可以将它们设为1,但如果您需要令牌,则应将令牌设为可选,但这并不能避免重复。structs Datum和DataClass都有let id。您可以将它们设为1,但如果您希望,则应将令牌设为可选。这是我正在查找的,不知道DecodingError.typeMismatch。非常感谢。当涉及到可编码时,你们总是能找到答案。这就是我一直在寻找的,不知道DecodingError.typeMismatch。非常感谢。当涉及到可编码的问题时,你总是能找到答案