问题描述
我了解如何为服务响应结构创建可编码包装类。 但是有时在服务器端,属性值会有所不同,它可能是Int或String。
示例
struct ResponseDataModel : Codable{
let data : DataClass?
enum CodingKey: String,CodingKey{
case data = "data"
}
init(from decoder: Decoder) throw {
let values = try decoder.container(keyedBy: CodingKeys.self)
data = try values.decodeIfPresent(DataClass.self,forKey:.data)
}
}
struct DataClass : Codable{
let id : Int
let name : String?
let age : Int?
enum CodingKey: String,CodingKey{
case id = "id"
case name = "name"
case age = "age"
}
init(from decoder: Decoder) throw {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decodeIfPresent(Int.self,forKey:.it)
name = try values.decodeIfPresent(String.self,forKey:.name)
age = try values.decodeIfPresent(Int.self,forKey:.age)
}
}
如果ID int字符串应该绑定到具有ID值数据的控制器,我想使用通用方式。
let id : <T>
如何用通用甲酸盐编写可编码。
解决方法
您可以使用以下模型进行操作:
struct ResponseDataModel<T: Codable>: Codable{
let data : DataClass<T>?
enum CodingKeys: String,CodingKey{
case data = "data"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
data = try values.decodeIfPresent(DataClass<T>.self,forKey:.data)
}
}
struct DataClass<T: Codable>: Codable {
let id: T?
let name: String?
let age: Int?
enum CodingKeys: String,CodingKey{
case id = "id"
case name = "name"
case age = "age"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decodeIfPresent(T.self,forKey:.id)
name = try values.decodeIfPresent(String.self,forKey:.name)
age = try values.decodeIfPresent(Int.self,forKey:.age)
}
}
但是,当您像这样调用id
的{{1}}函数时,应该始终知道decode(_:from:)
属性的类型:
JSONDecoder
或者您可以使用以下模型将let decoder = JSONDecoder()
do {
let decoded = try decoder.decode(ResponseDataModel<Int>.self,from: data)
print(decoded)
} catch {
print(error)
}
始终映射为id
,即使您的服务器将其作为Int
发送也是如此:
String
,
按照提供的@Joakim Danielson示例,尝试对每种类型的值进行解码即可达到所需的结果。
struct Response: Decodable {
let id: String
let name: String?
let age: Int?
private enum CodingKeys: String,CodingKey {
case data
}
private enum NestedCodingKeys: String,CodingKey {
case id
case name
case age
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let nestedContainer = try container.nestedContainer(
keyedBy: NestedCodingKeys.self,forKey: .data
)
if let id = try? nestedContainer.decode(Int.self,forKey: .id) {
self.id = String(id)
} else {
id = try nestedContainer.decode(String.self,forKey: .id)
}
name = try nestedContainer.decodeIfPresent(String.self,forKey: .name)
age = try nestedContainer.decodeIfPresent(Int.self,forKey: .age)
}
}
作为@gcharita illustrated,您也可以捕获DecodingError
,但是decode(_:forKey:)
的do-catch语句只能作为提前退出,因为它会引发以下情况之一错误-该特定键值对的typeMismatch
,keyNotFound
或valueNotFound
。
首先,这是使用Codable
进行解析时要注意的一些关键点。
-
如果属性名和键名完全相同,则不必每次都执行
enum CodingKeys
。 -
此外,如果没有特定的解析要求,则无需实现
init(from:)
。如果模型按照格式正确编写,Codable
将自动处理所有解析。
因此,通过上述两项改进,您的ResponseDataModel
看起来像
struct ResponseDataModel : Codable{
let data: DataClass?
}
现在,对于DataClass
,您只需要添加一个if-else
条件即可处理Int
和String
情况。这里不需要实现泛型。
使用String
或Int
作为id
的类型。并相应地添加条件。在下面的代码中,我将id
用作String
。
struct DataClass : Codable {
let id : String //here....
let name : String?
let age : Int?
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decodeIfPresent(String.self,forKey: .name)
age = try values.decodeIfPresent(Int.self,forKey: .age)
if let id = try? values.decode(Int.self,forKey: .id) {
self.id = String(id)
} else {
self.id = try values.decode(String.self,forKey:.id)
}
}
}