Groot 提供了一种简单的方法将 Core Data 对象图序列化为 JSON 或从 JSON 反序列化。
它使用 Core Data 模型中的 注释 来执行序列化,并提供了以下功能
NSValueTransformer 对象进行值转换。考虑以下描述知名漫画图书人物的 JSON
{
"id": "1699",
"name": "Batman",
"real_name": "Bruce Wayne",
"powers": [
{
"id": "4",
"name": "Agility"
},
{
"id": "9",
"name": "Insanely Rich"
}
],
"publisher": {
"id": "10",
"name": "DC Comics"
}
}我们可以使用三个实体: Character、Power 和 Publisher 来将此翻译成 Core Data 模型。
Groot 依赖于与实体、属性和关系关联的用户信息字典中存在的某些键值对来将管理对象序列化为 JSON 或从 JSON 反序列化。这些键值对在文档中通常被称为 注释。
在我们的例子中,我们应该在用户信息字典中添加一个 JSONKeyPath,用于每个属性和关系,指定 JSON 中的相应键路径
id 用于 identifier 属性,name 用于 name 属性,real_name 用于 realName 属性,powers 用于 powers 关系,publisher 用于 publisher 关系,没有 JSONKeyPath 条目的属性和关系在 JSON 序列化或反序列化时不会被考虑。
当创建模型时,我们决定使用 Integer 64 来表示我们的 identifier 属性。问题是,出于兼容性的原因,JSON 使用字符串表示 id 值。
我们可以在每个 identifier 属性的用户信息字典中添加一个 JSONTransformerName 条目,指定将字符串转换为数字的值转换器名称。
Groot 提供了一种创建并注册命名值转换器的简单方法
// Swift
func toString(_ value: Int) -> String? {
return String(value)
}
func toInt(_ value: String) -> Int? {
return Int(value)
}
ValueTransformer.setValueTransformer(withName: "StringToInteger", transform: toString, reverseTransform: toInt)// Objective-C
[NSValueTransformer grt_setValueTransformerWithName:@"StringToInteger" transformBlock:^id(NSString *value) {
return @([value integerValue]);
} reverseTransformBlock:^id(NSNumber *value) {
return [value stringValue];
}];为了在将JSON中的受管理对象序列化时保留对象图和避免重复信息,Groot需要了解如何唯一识别您的模型对象。
在我们的例子中,我们应该向Character、Power和Publisher实体的用户字典中添加一个带有值identifier的identityAttributes条目。
有关关于注释您的模型的更多信息,请参阅注释。
现在我们已准备好Core Data模型,我们可以开始添加一些数据了。
// Swift
let batmanJSON: JSONDictionary = [
"name": "Batman",
"id": "1699",
"powers": [
[
"id": "4",
"name": "Agility"
],
[
"id": "9",
"name": "Insanely Rich"
]
],
"publisher": [
"id": "10",
"name": "DC Comics"
]
]
do {
let batman: Character = try object(fromJSONDictionary: batmanJSON, inContext: context)
} catch let error as NSError {
// handle error
}// Objective-C
Character *batman = [GRTJSONSerialization objectWithEntityName:@"Character"
fromJSONDictionary:batmanJSON
inContext:self.context
error:&error];如果我们想要更新刚刚创建的对象,Groot可以为我们合并更改。
// Swift
let updateJSON: JSONDictionary = [
"id": "1699",
"real_name": "Bruce Wayne",
]
do {
// This will return the previously created managed object
let batman: Character = try object(fromJSONDictionary: updateJSON, inContext: context)
} catch let error as NSError {
// handle error
}假设我们的API不返回关系的完整对象,而仅返回标识符。
我们不需要更改我们的模型来支持这种情况。
// Swift
let batmanJSON: JSONDictionary = [
"name": "Batman",
"real_name": "Bruce Wayne",
"id": "1699",
"powers": ["4", "9"],
"publisher": "10"
]
do {
let batman: Character = try object(fromJSONDictionary: batmanJSON, inContext: context)
} catch let error as NSError {
// handle error
}上面的代码创建了一个完整的Character对象以及对应指向只有标识符属性的Power和Publisher对象的相应关系。
我们可以从不同的JSON对象中导入力量和发行商,Groot将很好地合并它们。
// Swift
let powersJSON: JSONArray = [
[
"id": "4",
"name": "Agility"
],
[
"id": "9",
"name": "Insanely Rich"
]
]
let publisherJSON: JSONDictionary = [
"id": "10",
"name": "DC Comics"
]
do {
let _: [Power] = try objects(fromJSONArray: powersJSON, inContext: context)
let _: Publisher = try object(fromJSONDictionary: publisherJSON, inContext: context)
} catch let error as NSError {
// handle error
}请注意,仅当指定了identityAttributes注释的值为**只有一个属性**的实体时,从标识符序列化关系才起作用。
有关更多序列化替代方案,请参阅Groot.swift和GRTJSONSerialization.h。
Groot通过entityMapperName注解支持实体继承。
如果您使用SQLite作为持久存储,Core Data通过为父实体和所有子实体创建一个包含所有其属性的超集的表来实现实体继承。如果实体中有大量数据,这可能会产生意想不到的性能后果,因此请谨慎使用此功能。
Groot提供将受管理对象转回到JSON的方法。
// Swift
let result = json(fromObject: batman)// Objective-C
NSDictionary *JSONDictionary = [GRTJSONSerialization JSONDictionaryFromObject:batman];有关更多序列化替代方案,请参阅Groot.swift和GRTJSONSerialization.h。
Guillermo Gonzalez
@gonzalezreal
Groot根据MIT许可提供。