Swift 如何以通用格式编写代码表

Swift 如何以通用格式编写代码表,swift,generics,codable,Swift,Generics,Codable,我已经了解了如何为服务响应结构创建可编码的包装器类。 但有时在服务器端,属性值会发生变化,它可能是Int或String 范例 struct ResponseDataModel : Codable{ let data : DataClass? enum CodingKey: String, CodingKey{ case data = "data" } init(from decoder: Decoder) throw {

我已经了解了如何为服务响应结构创建可编码的包装器类。 但有时在服务器端,属性值会发生变化,它可能是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>
let id:

如何以通用格式编写代码表。

您可以使用以下模型完成此操作:

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
映射为
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)
    }
}
根据提供的示例,您可以通过尝试解码每种类型的值来达到所需的结果

struct响应:可解码{
let id:String
名称:字符串?
让年龄:Int?
私有枚举编码键:字符串,编码键{
案例数据
}
私有枚举嵌套编码键:字符串,编码键{
病例id
案例名称
病例年龄
}
init(来自解码器:解码器)抛出{
let container=try decoder.container(keyedBy:CodingKeys.self)
让nestedContainer=try container.nestedContainer(
keyedBy:NestedCodingKeys.self,
forKey:。数据
)
如果let id=try?nestedContainer.decode(Int.self,forKey:.id){
self.id=字符串(id)
}否则{
id=尝试nestedContainer.decode(String.self,forKey:.id)
}
name=尝试nestedContainer.decodeIfPresent(String.self,forKey:.name)
age=尝试nestedContainer.decodeIfPresent(Int.self,forKey:.age)
}
}

您也可以捕获
DecodingError
,但是对于特定的键值对,
decode(u:forKey:)
的catch语句只起到提前退出的作用,因为它会抛出以下错误之一-
typemissmatch
keyNotFound
valueNotFound
,以下是使用
Codable
进行解析时需要注意的一些要点

  • 如果属性名称和键具有完全相同的名称,则无需每次都实现
    enum CodingKeys

  • 另外,如果没有特定的解析需求,则无需实现
    init(from:)
    <如果模型按照格式正确编写,code>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)
            }
        }
    }
    

    这回答了你的问题吗?使用这种逻辑,您不需要事先知道它是否是一个Int或String,您需要使用泛型解决方案,这将不会产生预期的结果。如果
    id
    String
    decodeIfPresent(\uuuuxforkey:)
    函数将抛出一个
    解码错误。类型不匹配
    错误以及
    else
    语句中的任何内容将永远不会执行。@gcharita更新了该错误。谢谢,这种方法(更新后)在应该抛出错误时会忽略格式错误的JSON。@gcharita不,不会。这将由catch语句在解析
    decode(u:forKey:)
    数据
    @gcharita do catch块时处理,因为它会抛出以下一个-
    类型不匹配
    ,因此只能作为提前退出,
    keyNotFound
    valueNotFound
    对于该特定的键值对,为什么不投票关闭此重复项?
    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)
            }
        }
    }