Расшифровка JSON вложенного словаря с использованием Decodable и сохранение его с использованием Core Data - PullRequest
0 голосов
/ 28 апреля 2020

У меня есть следующая структура JSON:

{
  "base": "USD",
  "date": "2020-04-24",
  "rates": {
    "CAD": 1.4049074074,
    "EUR": 0.9259259259
  }
}

Я бы хотел разобрать ее в классе и сохранить в Core Data:

class CurrencyRate: NSManagedObject, Decodable {
    enum CodingKeys: String, CodingKey {
        case base
        case date
        case rates
    }

    @NSManaged public var base: String
    @NSManaged public var date: Date
    @NSManaged public var rates: [String:Double]

    required convenience init(from decoder: Decoder) throws {
        ...
    }

Из чего Я читаю для того, чтобы сохранить поле rates в Базовых данных, мне нужно создать другую сущность, подобную этой, и создать отношение один ко многим:

class SubCurrencyRate: NSManagedObject, Decodable {
    enum CodingKeys: String, CodingKey {
        case currency
        case rate
    }

    @NSManaged public var currency: String
    @NSManaged public var rate: Double

    required convenience init(from decoder: Decoder) throws {
        ...
    }

И поле rates в CurrencyRate класс будет: @NSManaged public var rates: [SubCurrencyRate]

Проблема в том, что я не знаю, как разобрать вложенные json с динамическими c ключами в эти классы. Как это можно сделать?

И как вообще я могу декодировать эту часть JSON:

"rates": {
  "CAD": 1.4049074074,
  "EUR": 0.9259259259
}

в множество SubCurrencyRate объектов?

Ответы [ 2 ]

1 голос
/ 28 апреля 2020

Не обязательно, чтобы SubCurrencyRate соответствовало Decodable, но вы должны объявить обратную связь (также в модели данных!).

class SubCurrencyRate: NSManagedObject {

    @NSManaged public var currency: String
    @NSManaged public var rate: Double
    @NSManaged public var currencyRate: CurrencyRate

Для возможности непосредственного анализа JSON в Базовых данных объявите два расширения

extension CodingUserInfoKey {
    static let context = CodingUserInfoKey(rawValue: "context")!
}

extension JSONDecoder {
    convenience init(context: NSManagedObjectContext) {
        self.init()
        self.userInfo[.context] = context
    }
}

В CurrencyRate вы должны объявить отношение ко-многим как Set<SubCurrencyRate>, в init(from decoder декодировать словарь скоростей и сопоставить его с SubCurrencyRate экземплярами. Если в модели правильно определены отношения, свойство и обратное отношение заполняются автоматически.

class CurrencyRate: NSManagedObject, Decodable {
    enum CodingKeys: String, CodingKey { case base, date, rates }

    @NSManaged public var base: String
    @NSManaged public var date: Date
    @NSManaged public var rates: Set<SubCurrencyRate>

    required convenience init(from decoder: Decoder) throws {

        guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError("Error: No object context!") }
        let entity = NSEntityDescription.entity(forEntityName: "CurrencyRate", in: context)!
        self.init(entity: entity, insertInto: context)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        base = try values.decode(String.self, forKey: .base)
        date = try values.decode(Date.self, forKey: .date)
        let ratesData = try values.decode([String:Double].self, forKey: .rates)
        for (key, value) in ratesData {
            let subCurrencyRate = SubCurrencyRate(context: context)
            subCurrencyRate.currency = key
            subCurrencyRate.rate = value
            subCurrencyRate.currencyRate = self
        }
    }
}

Для декодирования JSON необходимо использовать API JSONDecoder(context: при передаче расширения. контекст управляемого объекта и для правильного декодирования date необходимо добавить соответствующий dateDecodingStrategy.

0 голосов
/ 28 апреля 2020

Я написал сообщение в блоге об этом когда-то go. Короче говоря: я бы рекомендовал использовать DTO для декодирования в модели CoreData. Учитывая тот факт, что модели CoreData всегда связаны с контекстом, которого у вас нет при декодировании с JSON, проще иметь простые структуры, представляющие ваши JSON, а затем создавать модели CoreData из этих структур.

...