The problem: decode an array of objects belonging to Parent and Child classes.
I read a lot of stuff on this subject but I have not been able to find a simple solution.
I encoded a type property which provide the information of the original class, but I haven't found a way to use it in decoding the object.
class Parent: Codable, CustomDebugStringConvertible {
var debugDescription: String {
return "[(name)]"
}
var name: String
init(name: String) {
self.name = name
}
enum CodingKeys: CodingKey {
case name
case type
case age
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try! container.decode(String.self, forKey: .name)
let type = try! container.decode(String.self, forKey: .type)
print("Reading (type)")
if type == "Child" {
try Child.init(from: decoder)
return
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode("Parent", forKey: .type)
try container.encode(name, forKey: .name)
}
}
class Child: Parent {
override var debugDescription: String {
return "[(name) - (age)]"
}
var age: Int
init(name: String, age: Int) {
self.age = age
super.init(name: name)
}
enum CodingKeys: CodingKey {
case name
case age
case type
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
age = try! container.decode(Int.self, forKey: .age)
let name = try! container.decode(String.self, forKey: .name)
super.init(name: name) // I think the problem is here!
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode("Child", forKey: .type)
try container.encode(age, forKey: .age)
}
}
This is the test code.
let array = [Parent(name: "p"), Child(name: "c",age: 2)]
print(array)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let decoder = JSONDecoder()
do {
let jsonData = try encoder.encode(array)
let s = String(data: jsonData, encoding: .ascii)
print("Json Data (s!)")
let decodedArray = try decoder.decode([Parent].self, from: jsonData)
print(decodedArray)
}
catch {
print(error.localizedDescription)
}
The output of the original array is:
[[p], [c - 2]]
The output of the decode array is:
[[p], [c]]
How do I change the Parent and/or the Child init function in order to correctly decode the object?
Clearly, my actual scenario is much more complex of this. I have to encode / decode a class which contains an array of classes with inheritance. I have tried to use this:
https://github.com/IgorMuzyka/Type-Preserving-Coding-Adapter
Apparently, it works fine on an array of Parent, Child but it doesn't if the array is inside another class.
Moreover, I would like to learn a solution to reuse in other cases and avoid including external library is not strictly needed.
See Question&Answers more detail:
os