簡體   English   中英

當超類也是可編碼的時,如何在子類中使用CodingKeys?

[英]How to use CodingKeys in sub-class when the super class is also Codable?

我正在研究JSON編碼和解碼,但是有些問題非常煩人,而且我不知道如何在繼承類中使用CodingKeys

我有兩個類ResponseBeanResponseWithObjectBean<T>

這是響應類的定義:

public class ResponseBean: Codable
{
    //This is only sample, I define `CodingKeys` because the property in json is in different name.
    private enum CodingKeys: String, CodingKey
    {
        case intA
        case intB
    }
    public var intA: Int32 = 0
    public var intB: Int32 = 0

}

public class ResponseWithObjectBean<T: Codable> : ResponseBean
{
    /*
    Here I don't know how to define an enum to confirm protocl CondingKey. 
    I defined an enum named CodingKeys or whatever, they just don't work and 
    the testMessage and obj are still nil.
    But if I implement the init(from decoder: Decoder) construction and manually
    pass the coding keys which I defined to the decode function, all works fine.
    */
    public var testMessage: String? = nil
    public var obj: T? = nil
}

我將從響應中得到一個用戶:

public class User: Codable
{
    private enum CodingKeys: String, CodingKey
    {
        case name
        case age
    }

    public var name: String? = nil
    public var age: Int32? = nil
}

這是測試json:

var testJson = """
{
    "intA": 10,
    "intB": 20,
    "testMessage": "This is a test json",
    "obj":{
        "name": "LiHong",
        "age": 11
    }
}
"""

以下是我的運行方式:

do{
    var responseData = testJson.data(using: .utf8)
    var decoder = JSONDecoder()
    var response: ResponseWithObjectBean<User> = try decoder.decode(ResponseWithObjectBean<User>.self, from: responseData)
}catch let e{

}

我不知道如何在ResponseWithObjectBean類中定義CodingKeys ,即使我這樣做,它也不起作用。 但是,如果我實現init(from decoder: Decoder) throws則會init(from decoder: Decoder) throws結構並手動傳遞在ResponseWithObjectBean定義的編碼鍵,則我可以獲得所有屬性。

這非常簡單,您只需要在子類中手動進行編碼和解碼即可:

public class ResponseWithObjectBean<T: Codable> : ResponseBean {
    public var testMessage: String? = nil
    public var obj: T? = nil

    // Create another CodingKey compliant enum with another name for the new keys
    private enum CustomCodingKeys: String, CodingKey {
        case testMessage
        case obj
    }

    // Override the decoder
    required init(from decoder: Decoder) throws {
        try super.init(from: decoder)

        let container = try decoder.container(keyedBy: CustomCodingKeys.self)

        testMessage = try container.decode(String?.self, forKey: .testMessage)
        obj = try container.decode(T?.self, forKey: .obj)
    }

    // And the coder
    public override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)

        var container = encoder.container(keyedBy: CustomCodingKeys.self)

        try container.encode(testMessage, forKey: .testMessage)
        try container.encode(obj, forKey: .obj)
    }

}

這樣,您可以對所需的方式進行解碼和編碼:

let decoder = JSONDecoder()
let response = try decoder.decode(ResponseWithObjectBean<User>.self, from: responseData)

let data = try JSONEncoder().encode(response)
print(String(data: data, encoding: .utf8))

編輯 :為了防止您手動編寫所有這些樣板,可以使用諸如Sourcery的生成工具: https : //github.com/krzysztofzablocki/Sourcery

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM