如何以通用格式编写代码
How to write the codable in generic format
我已经了解如何为服务响应结构制作可编码包装器 class。
但有时在服务器端属性值会有所不同,它可能是 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)
}
}
但是,当您像这样调用 JSONDecoder
的 decode(_:from:)
函数时,您应该始终知道 id
属性 的类型:
let decoder = JSONDecoder()
do {
let decoded = try decoder.decode(ResponseDataModel<Int>.self, from: data)
print(decoded)
} catch {
print(error)
}
或者您可以使用以下模型始终将 id
映射为 Int
,即使您的服务器将其发送为 String
:
struct ResponseDataModel: Codable{
let data : DataClass?
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.self, forKey:.data)
}
}
struct DataClass: Codable {
let id: Int?
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)
do {
id = try values.decodeIfPresent(Int.self, forKey:.id)
} catch DecodingError.typeMismatch {
if let idString = try values.decodeIfPresent(String.self, forKey:.id) {
id = Int(idString)
} else {
id = nil
}
}
name = try values.decodeIfPresent(String.self, forKey:.name)
age = try values.decodeIfPresent(Int.self, forKey:.age)
}
}
根据提供的 @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 一样,您也可以捕获 DecodingError
,但是 do-catch 对 decode(_:forKey:)
的语句只会作为提前退出,因为它会抛出以下错误之一- typeMismatch
、keyNotFound
或 valueNotFound
用于特定的 key-value 对。
首先,这里有一些使用Codable
进行解析时需要注意的关键点。
如果 属性 名称和键的名称完全相同,则无需每次都执行 enum CodingKeys
。
另外,如果没有特定的解析需求,则不需要实现init(from:)
。 Codable
如果模型按照格式正确编写,将自动处理所有解析。
因此,通过以上 2 项改进,您的 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)
}
}
}
我已经了解如何为服务响应结构制作可编码包装器 class。 但有时在服务器端属性值会有所不同,它可能是 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)
}
}
但是,当您像这样调用 JSONDecoder
的 decode(_:from:)
函数时,您应该始终知道 id
属性 的类型:
let decoder = JSONDecoder()
do {
let decoded = try decoder.decode(ResponseDataModel<Int>.self, from: data)
print(decoded)
} catch {
print(error)
}
或者您可以使用以下模型始终将 id
映射为 Int
,即使您的服务器将其发送为 String
:
struct ResponseDataModel: Codable{
let data : DataClass?
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.self, forKey:.data)
}
}
struct DataClass: Codable {
let id: Int?
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)
do {
id = try values.decodeIfPresent(Int.self, forKey:.id)
} catch DecodingError.typeMismatch {
if let idString = try values.decodeIfPresent(String.self, forKey:.id) {
id = Int(idString)
} else {
id = nil
}
}
name = try values.decodeIfPresent(String.self, forKey:.name)
age = try values.decodeIfPresent(Int.self, forKey:.age)
}
}
根据提供的 @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 DecodingError
,但是 do-catch 对 decode(_:forKey:)
的语句只会作为提前退出,因为它会抛出以下错误之一- typeMismatch
、keyNotFound
或 valueNotFound
用于特定的 key-value 对。
首先,这里有一些使用Codable
进行解析时需要注意的关键点。
如果 属性 名称和键的名称完全相同,则无需每次都执行
enum CodingKeys
。另外,如果没有特定的解析需求,则不需要实现
init(from:)
。Codable
如果模型按照格式正确编写,将自动处理所有解析。
因此,通过以上 2 项改进,您的 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)
}
}
}