Я сериализую и десериализирую унаследованные классы, используя протокол Codable.Я успешно могу сериализовать всю модель в JSON, но у меня возникают проблемы с десериализацией JSON.Это моя структура данных выглядит так.Некоторая часть моего приложения работает, как в приведенном ниже примере, и в этот момент нам будет слишком сложно изменить всю структуру данных.
class Base: Codable {
let baseValue: String
init(baseValue :String) {
self.baseValue = baseValue
}
enum SuperCodingKeys: String, CodingKey {
case baseValue
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: SuperCodingKeys.self)
try container.encode(baseValue, forKey: .baseValue)
}
}
class Child1: Base {
let child1Value: Int
init(child1Value: Int, baseValue: String) {
self.child1Value = child1Value
super.init(baseValue: baseValue)
}
private enum CodingKeys: String, CodingKey {
case child1Value
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.child1Value = try container.decode(Int.self, forKey: .child1Value)
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(child1Value, forKey: .child1Value)
try super.encode(to: encoder)
}
}
class Child2: Base {
let child2Value: Int
init(child2Value: Int, baseValue: String) {
self.child2Value = child2Value
super.init(baseValue: baseValue)
}
private enum CodingKeys: String, CodingKey {
case child2Value
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.child2Value = try container.decode(Int.self, forKey: .child2Value)
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(child2Value, forKey: .child2Value)
try super.encode(to: encoder)
}
}
class DataManager: Codable {
var bases: [Base] = []
init(bases: [Base]) {
self.bases = bases
}
}
Так я добавляю значение в DataManger (кстати, в моем приложении это одноэлементный класс, который содержит все данные для приложения. Я пытаюсь реализовать протокол Codable для этого класса DataManager)
let child1 = Child1(child1Value: 1, baseValue: "Child1Value_Base")
let child2 = Child2(child2Value: 2, baseValue: "Child2Value_Base")
let dataManager = DataManager(bases: [])
dataManager.bases.append(child1 as Base)
dataManager.bases.append(child2 as Base)
Я использую JSONEncoder () для кодирования модели в данные с использованием этого кода.До этого момента все работало нормально.
let dataManagerData = try JSONEncoder().encode(dataManager)
print(String(data: dataManagerData, encoding: .utf8))
Вот как выглядит json после кодирования
{
"bases":[{
"child1Value":1,
"baseValue":"Child1Value_Base"
},
{
"child2Value":2,
"baseValue":"Child2Value_Base"
}]
}
Так что, когда я пытаюсь декодировать этот JSON с помощью приведенного ниже кода, я могу толькодекодировать его до базового (родительского класса) уровня, а не до дочернего уровня.
let dataManager = try JSONDecoder().decode(DataManager.self, from: dataManagerData)
И это то, что я могу получить из этого.
{
"bases":[{
"baseValue":"Child1Value_Base"
},
{
"baseValue":"Child2Value_Base"
}]
}
Чтобы решить эту проблему, я попытался вручную декодировать, используя этот способ, но JSONDecoder дает мне 0 отсчетов оснований.
class DataManager: Codable {
var bases: [Base] = []
init(bases: [Base]) {
self.bases = bases
}
private enum CodingKeys: String, CodingKey {
case bases
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
// I think decoder is trying to decode the bases object based on Child1 or Child2 class and it fail.
if let value = try? container.decode(Child1.self, forKey: .bases) {
bases.append(value as Base)
} else if let value = try? container.decode(Child2.self, forKey: .bases){
bases.append(value as Base)
}
} catch {
print(error)
}
}
}
Итак, мои вопросы
- как десериализовать этот массив баз на основе их соответствующего дочернего класса и добавить их в DataManager?
- Есть ли какие-либопуть в методе «init (from decoder: Decoder)», в котором мы можем получить значение ключа и последовательно перебрать их для декодирования в соответствующий класс.