如何以通用格式编写编码

问题描述

我了解如何为服务响应结构创建可编码包装类。 但是有时在服务器端,属性值会有所不同,它可能是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语句只能作为提前退出,因为它会引发以下情况之一错误-该特定键值对的typeMismatchkeyNotFoundvalueNotFound

,

首先,这是使用Codable进行解析时要注意的一些关键点。

  1. 如果属性名和键名完全相同,则不必每次都执行enum CodingKeys

  2. 此外,如果没有特定的解析要求,则无需实现init(from:)。如果模型按照格式正确编写,Codable将自动处理所有解析。

因此,通过上述两项改进,您的ResponseDataModel看起来像

struct ResponseDataModel : Codable{
    let data: DataClass?
}

现在,对于DataClass,您只需要添加一个if-else条件即可处理IntString情况。这里不需要实现泛型

使用StringInt作为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)
        }
    }
}