如何手动解码符合自定义协议的项目数组?
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 属性.
确定预期类型后将解码委托给它们
我正在尝试解码符合自定义协议 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 属性.