使用 Decodable 解码 JSON 嵌套字典并使用 Core Data 存储它
Posted
技术标签:
【中文标题】使用 Decodable 解码 JSON 嵌套字典并使用 Core Data 存储它【英文标题】:Decoding JSON nested dictionary using Decodable and storing it using Core Data 【发布时间】:2020-04-28 12:09:02 【问题描述】:我有以下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
...
根据我阅读的内容,为了在 Core Data 中存储 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
...
CurrencyRate
类中的字段 rates
将是:@NSManaged public var rates: [SubCurrencyRate]
问题是我不知道如何将带有动态键的嵌套 json 解析到这些类中。怎么办?
一般来说我如何解码这部分 JSON:
"rates":
"CAD": 1.4049074074,
"EUR": 0.9259259259
进入 许多 SubCurrencyRate
对象?
【问题讨论】:
创建新实体并添加关系时,您不需要为此(费率)的特定属性,因为该关系将充当费率的属性。从建模的角度来看,尽管将数据分解为货币对和汇率可能会更好,然后您只需要一个实体。 【参考方案1】:SubCurrencyRate
不必符合Decodable
,但您必须声明反向关系(也在数据模型中!)。
class SubCurrencyRate: NSManagedObject
@NSManaged public var currency: String
@NSManaged public var rate: Double
@NSManaged public var currencyRate: CurrencyRate
为了能够在 Core Data 中直接解析 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,您必须在传递托管对象上下文的扩展中使用 JSONDecoder(context:
API 并正确解码 date
,您必须添加适当的 dateDecodingStrategy
。
【讨论】:
【参考方案2】:前段时间我写了一封blog post 来讨论这个问题。 长话短说:我建议使用 DTO 解码成 CoreData 模型。鉴于 CoreData 模型总是绑定到一个上下文(在从 JSON 解码时您没有)这一事实 - 拥有表示 JSON 的简单结构然后从这些结构创建您的 CoreData 模型会更容易。
【讨论】:
以上是关于使用 Decodable 解码 JSON 嵌套字典并使用 Core Data 存储它的主要内容,如果未能解决你的问题,请参考以下文章
当属性类型可能从 Int 更改为 String 时,如何使用 Decodable 协议解析 JSON?
Swift 4 Decodable - 将 JSON 对象解码为“数据”