【问题标题】:Polymorphically decode a sub-object of a KeyedDecodingContainer in Swift在 Swift 中多态地解码 KeyedDecodingContainer 的子对象
【发布时间】:2020-02-27 00:11:45
【问题描述】:

以下代码尝试创建一个Codable 类型AnyBase,它允许您对Base 的各种子类进行编码和解码。如所写,in 失败,因为它尝试使用getTypeFromCode 通过字符串代码查找类型对象。但是,如果您使用注释掉的部分而不是硬编码类型,它会根据需要打印“Sub1”。

有没有办法调整它以使用 getTypeFromCode 之类的东西并消除 init 中的硬编码类型?

class Base: Codable {
    var foo: Int = 1
    var codingTypeKey: String { fatalError("abstract") }
}

class Sub1: Base {
    var sub1Prop: Int = 2
    override var codingTypeKey: String { "sub1" }
    enum CodingKeys: CodingKey {
        case sub1Prop
    }
    override init() {
        super.init()
    }
    required init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        sub1Prop = try c.decode(Int.self, forKey: .sub1Prop)
        try super.init(from: decoder)
    }
    override func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        try c.encode(sub1Prop, forKey: .sub1Prop)
        try super.encode(to: encoder)
    }
}
class Sub2: Base {
    var sub2Prop: Int = 2
    override var codingTypeKey: String { "sub2" }
}

func getTypeFromCode(_ typeCode: String) -> Base.Type {
    if typeCode == "sub1" { return Sub1.self }
    else if typeCode == "sub2" { return Sub2.self }
    else { fatalError("bad type code") }
}

class AnyBase: Codable {
    let base: Base
    init(_ b: Base) { base = b }
    required init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        let typeCode = try c.decode(String.self, forKey: .type)

        /*if typeCode == "sub1" {
            self.base = try c.decode(Sub1.self, forKey: .base)
        } else if typeCode == "sub2" {
            self.base = try c.decode(Sub2.self, forKey: .base)
        } else {
            fatalError("bad type code")
        }*/

        let typeObj = getTypeFromCode(typeCode)
        self.base = try c.decode(typeObj, forKey: .base)
    }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        try c.encode(base.codingTypeKey, forKey: .type)
        try c.encode(base, forKey: .base)
    }

    enum CodingKeys: CodingKey {
        case base, type
    }
}

// To to round-trip encode/decode and get the right object back...
let enc = JSONEncoder()
let dec = JSONDecoder()
let sub = Sub1()
let any = AnyBase(sub)
let data = try! enc.encode(any)
let str = String(data:data, encoding:.utf8)!
print(str)
let any2 = try! dec.decode(AnyBase.self, from: data)
print(type(of:any2.base))

【问题讨论】:

    标签: swift codable


    【解决方案1】:

    代替getTypeFromCode (_:)方法,你可以创建一个enum BaseTypelike,

    enum BaseType: String {
        case sub1, sub2
    
        var type: Base.Type {
            switch self {
            case .sub1: return Sub1.self
            case .sub2: return Sub2.self
            }
        }
    }
    

    现在,在init(from:) 中使用typeCode 作为rawValue 获取BaseType,即

    class AnyBase: Codable {
        required init(from decoder: Decoder) throws {
            let c = try decoder.container(keyedBy: CodingKeys.self)
            let typeCode = try c.decode(String.self, forKey: .type)
            if let baseType = BaseType(rawValue: typeCode) {
                self.base = try c.decode(baseType.type, forKey: .base)
            } else {
                fatalError("bad type code")
            }
        }
        //rest of the code...
    }
    

    【讨论】:

    • 我刚刚测试了它,但它不起作用。就像我未注释的示例一样,它会根据需要解码并创建 Base,而不是 Sub1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多