Для классов это не так просто, как для структур, но реализовать этот тип Decodable все же довольно просто.
struct ElementContainer: Decodable {
let elements: [Base]
enum CodingKeys: String, CodingKey { case elements }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.elements = try container.decode([Element].self, forKey: .elements)
.map { $0.base }
}
}
struct Element: Decodable {
let base: Base
enum CodingKeys: String, CodingKey {
case type
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
switch type {
case "A":
base = try A(from: decoder)
case "B":
base = try B(from: decoder)
default:
throw DecodingError.dataCorruptedError(forKey: .type,
in: container,
debugDescription: "Unknown type: \(type)")
}
}
}
class Base {}
class A : Base, Decodable {
let a: Int
init(a:Int) {
self.a = a
super.init()
}
}
class B : Base, Decodable {
let b: Int
let c: Int
init(b:Int, c:Int) {
self.b = b
self.c = c
super.init()
}
}
let results = try JSONDecoder().decode(ElementContainer.self, from: json).elements
Структуры немного проще, и вы можете избавиться от оператора switch , С классами это сделать сложнее, потому что он вводит необходимые начальные элементы, которые утомительно реализовывать.
struct ElementContainer: Decodable {
let elements: [Element]
}
struct Element: Decodable {
let base: Base
enum CodingKeys: String, CodingKey {
case type
}
static let mapping: [String: Base.Type] = ["A": A.self, "B": B.self]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
guard let elementType = Element.mapping[type] else {
throw DecodingError.dataCorruptedError(forKey: .type,
in: container,
debugDescription: "Unknown type: \(type)")
}
base = try elementType.init(from: decoder)
}
}
protocol Base: Decodable {}
struct A : Base {
let a: Int
}
struct B : Base {
let b: Int
let c: Int
}