【问题标题】:Codable with inheritance可通过继承编码
【发布时间】:2020-01-25 21:26:57
【问题描述】:

我正在使用 Codable 协议对继承的类进行序列化和反序列化。我成功地将整个模型序列化为 JSON,但在反序列化 JSON 时遇到了麻烦。这是我的数据结构的样子。我的应用程序的某些部分像下面的示例一样工作,此时更改整个数据结构对我们来说工作量太大。

class Base: Codable {
     let baseValue: String
     init(baseValue :String) {
         self.baseValue = baseValue
     }
     enum SuperCodingKeys: String, CodingKey {
         case baseValue
     }

     func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: SuperCodingKeys.self)
        try container.encode(baseValue, forKey: .baseValue)
     }
}

class Child1: Base {
    let child1Value: Int
    init(child1Value: Int, baseValue: String) {
        self.child1Value = child1Value
        super.init(baseValue: baseValue)
    }

    private enum CodingKeys: String, CodingKey {
        case child1Value
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.child1Value = try container.decode(Int.self, forKey: .child1Value)
        try super.init(from: decoder)
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(child1Value, forKey: .child1Value)
        try super.encode(to: encoder)

    }

}

class Child2: Base {
    let child2Value: Int
    init(child2Value: Int, baseValue: String) {
        self.child2Value = child2Value
        super.init(baseValue: baseValue)
    }


    private enum CodingKeys: String, CodingKey {
        case child2Value
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.child2Value = try container.decode(Int.self, forKey: .child2Value)
        try super.init(from: decoder)
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(child2Value, forKey: .child2Value)
        try super.encode(to: encoder)

    }
}


class DataManager: Codable {
    var bases: [Base] = []
    init(bases: [Base]) {
        self.bases = bases
    }
}

这就是我为 DataManger 添加价值的方式(顺便说一句,在我的应用程序中,这是一个保存应用程序全部数据的单例类。我正在尝试在这个 DataManager 类上实现 Codable 协议)

let child1 = Child1(child1Value: 1, baseValue: "Child1Value_Base")
let child2 = Child2(child2Value: 2, baseValue: "Child2Value_Base")

let dataManager = DataManager(bases: [])

dataManager.bases.append(child1 as Base)
dataManager.bases.append(child2 as Base)

我正在使用 JSONEncoder() 通过此代码将模型编码为数据。到目前为止一切正常。

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

这是 json 编码后的样子

{
    "bases":[{
        "child1Value":1,
        "baseValue":"Child1Value_Base"
    },
    {
        "child2Value":2,
        "baseValue":"Child2Value_Base"
    }]
}

因此,当我尝试使用以下代码解码此 JSON 时,我只能将其解码到 Base(父类)级别,而不是子级别。

let dataManager = try JSONDecoder().decode(DataManager.self, from: dataManagerData)

这就是我能够摆脱的。

{
    "bases":[{
        "baseValue":"Child1Value_Base"
    },
    {
        "baseValue":"Child2Value_Base"
    }]
}

为了解决这个问题,我尝试使用这种方式手动解码,但 JSONDecoder 给了我 0 个碱基计数。

class DataManager: Codable {
    var bases: [Base] = []
    init(bases: [Base]) {
        self.bases = bases
    }
    private enum CodingKeys: String, CodingKey {
        case bases
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            // I think decoder is trying to decode the bases object based on Child1 or Child2 class and it fail. 
            if let value = try? container.decode(Child1.self, forKey: .bases) {
                bases.append(value as Base)
            } else if let value = try? container.decode(Child2.self, forKey: .bases){
                bases.append(value as Base)
            }
        } catch  {
            print(error)
        }
    }
}

所以我的问题是

  1. 如何根据各自的子类反序列化这组基数组并将它们添加到 DataManager 中?
  2. “init(来自解码器:解码器)”方法中是否有任何方法可以获取键的值并逐个遍历它们以解码到各自的类。

【问题讨论】:

    标签: ios json swift codable decodable


    【解决方案1】:

    如果有人遇到同样的问题,我在下面的解决方案中找到了。

    class DataManager: Codable {
        var bases: [Base] = []
        init(bases: [Base]) {
            self.bases = bases
        }
        private enum CodingKeys: String, CodingKey {
            case bases
        }
    
        required init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            var list = try container.nestedUnkeyedContainer(forKey: DataManager.CodingKeys.bases)
    
            while !list.isAtEnd {
                if let child1 = try? list.decode(Child1.self) {
                    bases.append(child1 as Base)
                } else if let child2 = try? list.decode(Child2.self) {
                    bases.append(child2 as Base)
                }
            }
        }
    }
    

    【讨论】:

    • 您确实应该创建和实施协议来完成这项工作。也许考虑重组您的代码以使其更“迅速”。我相信它在这里更加突出,因为这看起来像是一系列模型,它们应该比经常是结构,因此只能从协议继承。
    猜你喜欢
    • 2018-06-22
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-18
    相关资源
    最近更新 更多