如何手动解码符合自定义协议的项目数组?

How to manually decode an array of items conforming to a custom protocol?

我正在尝试解码符合自定义协议 LogicBlock 的项目数组 logicBlocks

struct MyModel: Codable {
    var var1: String
    var var2: Int
    var logicBlocks: [LogicBlock]
}

我查看了 ,它询问如何解码项目数组,但是我有一个包含如下枚举的协议:

enum LogicBlockTypeID: Int, Codable {
    case a = 500
    case b = 501
    case c = 502
}

protocol LogicBlock: Codable {
    var typeID: LogicBlockTypeID { get }
}

和结构实现 LogicBlock 像这样:

struct Struct1: LogicBlock {
    var someVar = 32
    var typeID = .a
}

struct Struct2: LogicBlock {
    var someString = "hello"
    var typeID = .b
}

struct Struct2: LogicBlock {
    var typeID = .c
}

我尝试使用 init(from decoder: Decoder)

解码 MyModel
var codeContainer = try values.nestedUnkeyedContainer(forKey: .code)
var parsedCode = [LogicBlock]()

enum codingKeys: String, CodingKey {
    ...
    case .logicBlocks
}

init(from decoder: Decoder) {
...
    var codeContainer = try values.nestedUnkeyedContainer(forKey: .code)
    var parsedCode = [LogicBlock]()
    while !codeContainer.isAtEnd {
        let nestedDecoder = try codeContainer.superDecoder()
        let block = try Program.decodeLogicBlock(from: nestedDecoder)
        parsedCode.append(block)
    }
}

private static func decodeLogicBlock(from decoder: Decoder) throws -> LogicBlock {
    let values = try decoder.container(keyedBy: LogicBlockTypeID.self)

    // SOMETHING TO DISTINGUISH WHAT TYPE WE HAVE

    return ...
}

我们如何才能知道我们在这里解码的对象类型?

您可以实现一个具体类型,即 AnyLogicBlock,它会公开两个变量 let typeID: LogicBlockTypeID let wrapped: LogicBlock

这是一个简化的例子:

enum TypeID: String, Codable {
    case a, b
}

protocol MyType {
    var type: TypeID { get }
}

struct MyConreteTypeA: MyType {
    var type: TypeID
    var someVar: Int
}

struct MyConreteTypeB: MyType {
    var type: TypeID
    var someString: String
}

struct AnyType: MyType, Decodable {
    private enum CodingKeys: String, CodingKey {
        case type, someVar, someString
    }

    let type: TypeID
    let wrapped: MyType

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        type = try container.decode(TypeID.self, forKey: .type)
        switch type {
        case .a:
            wrapped = MyConreteTypeA(type: type, someVar: try container.decode(Int.self, forKey: .someVar))
        case .b:
            wrapped = MyConreteTypeB(type: type, someString: try container.decode(String.self, forKey: .someString))
        }
    }
}

var json = #"""
[
  { "type": "a", "someVar": 1 },
  { "type": "b", "someString": "test" }
]
"""#

let result = try! JSONDecoder().decode([AnyType].self, from: json.data(using: .utf8)!)

for item in result {
    switch item.type {
    case .a:
        let casted = item.wrapped as! MyConreteTypeA
        print("MyConreteTypeA: \(casted)")
    case .b:
        let casted = item.wrapped as! MyConreteTypeB
        print("MyConreteTypeB: \(casted)")
    }
}

或者,您可以为具体类型实现 Decodable,并在根据类型 ID 属性.

确定预期类型后将解码委托给它们