iPhone 上的 JSON 和核心数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2362323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JSON and Core Data on the iPhone
提问by Urizen
I have a core data object graph (consisting of two entities linked by a to-many relationship).
我有一个核心数据对象图(由一对多关系链接的两个实体组成)。
I was curious, as a relatively inexperienced iPhone developer, whether anyone could recommend an approach, and a suitable JSON implementation for the iPhone, which would allow me to:
我很好奇,作为一个相对缺乏经验的 iPhone 开发人员,是否有人可以推荐一种方法,以及适用于 iPhone 的合适的 JSON 实现,这将使我能够:
convert the core data records into a JSON string (whilst maintaining the relationship between the entities); and
convert the JSON string back into core data objects (again preserving the relationship between entities).
将核心数据记录转换为 JSON 字符串(同时保持实体之间的关系);和
将 JSON 字符串转换回核心数据对象(再次保留实体之间的关系)。
I have searched, unsuccessfully, for a tutorial/code sample on this point so any assistance would be gratefully received.
我已经在这一点上搜索了教程/代码示例,但未成功,因此将不胜感激地收到任何帮助。
回答by Marcus S. Zarra
First, pick a JSON library to use, I personally like TouchJSON but several others out there are quite nice as well. The complicated part, although not very hard, is to convert your managed objects into suitable structures for the conversion. I wrote this real quick so it may have an error or two :)
首先,选择一个要使用的 JSON 库,我个人喜欢 TouchJSON,但其他几个库也很不错。复杂的部分虽然不是很困难,但将您的托管对象转换为适合转换的结构。我写得非常快,所以它可能有一个或两个错误:)
The methods you call are:
你调用的方法是:
- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects;
- (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc;
And the implementation is as follows:
实现如下:
- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject*)managedObject
{
NSDictionary *attributesByName = [[managedObject entity] attributesByName];
NSDictionary *relationshipsByName = [[managedObject entity] relationshipsByName];
NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
[valuesDictionary setObject:[[managedObject entity] name] forKey:@"ManagedObjectName"];
for (NSString *relationshipName in [relationshipsByName allKeys]) {
NSRelationshipDescription *description = [[[managedObject entity] relationshipsByName] objectForKey:relationshipName];
if (![description isToMany]) {
NSManagedObject *relationshipObject = [managedObject valueForKey:relationshipName];
[valuesDictionary setObject:[self dataStructureForManagedObject:relationshipObject] forKey:relationshipName];
continue;
}
NSSet *relationshipObjects = [managedObject objectForKey:relationshipName];
NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];
for (NSManagedObject *relationshipObject in relationshipObjects) {
[relationshipArray addObject:[self dataStructureForManagedObject:relationshipObject]];
}
[valuesDictionary setObject:relationshipArray forKey:relationshipName];
}
return [valuesDictionary autorelease];
}
- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
for (NSManagedObject *managedObject in managedObjects) {
[dataArray addObject:[self dataStructureForManagedObject:managedObject]];
}
return [dataArray autorelease];
}
- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects
{
NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
NSString *jsonString = [[CJSONSerializer serializer] serializeArray:objectsArray];
return jsonString;
}
- (NSManagedObject*)managedObjectFromStructure:(NSDictionary*)structureDictionary withManagedObjectContext:(NSManagedObjectContext*)moc
{
NSString *objectName = [structureDictionary objectForKey:@"ManagedObjectName"];
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:objectName inManagedObjectContext:moc];
[managedObject setValuesForKeysWithDictionary:structureDictionary];
for (NSString *relationshipName in [[[managedObject entity] relationshipsByName] allKeys]) {
NSRelationshipDescription *description = [relationshipsByName objectForKey:relationshipName];
if (![description isToMany]) {
NSDictionary *childStructureDictionary = [structureDictionary objectForKey:relationshipName];
NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
[managedObject setObject:childObject forKey:relationshipName];
continue;
}
NSMutableSet *relationshipSet = [managedObject mutableSetForKey:relationshipName];
NSArray *relationshipArray = [structureDictionary objectForKey:relationshipName];
for (NSDictionary *childStructureDictionary in relationshipArray) {
NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
[relationshipSet addObject:childObject];
}
}
return managedObject;
}
- (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc
{
NSError *error = nil;
NSArray *structureArray = [[CJSONDeserializer deserializer] deserializeAsArray:json error:&error];
NSAssert2(error == nil, @"Failed to deserialize\n%@\n%@", [error localizedDescription], json);
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
for (NSDictionary *structureDictionary in structureArray) {
[objectArray addObject:[self managedObjectFromStructure:structureDictionary withManagedObjectContext:moc]];
}
return [objectArray autorelease];
}
Now this is recursive so you can easily end up translating your entire persistent store if you are not careful. Watch your relationships and make sure that they only go "down" the object tree so that you only get the objects you want translated.
现在这是递归的,因此如果您不小心,您很容易最终翻译整个持久存储。注意您的关系并确保它们只在对象树中“向下”,以便您只获得您想要翻译的对象。
回答by creativeKoder
I just wanted to point out a small typo, that caused the code to crash, and hopefully this will save you a few min.
我只是想指出一个导致代码崩溃的小错误,希望这可以为您节省几分钟。
- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects {
NSMutableArray *dataArray = [[NSArray alloc] init];
for (NSManagedObject *managedObject in managedObjects) {
[dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
}
return [dataArray autorelease];
}
The NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray
这 NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray
really should be NSMutableArray *dataArray = [[NSMutableArray alloc] init];
真的应该 NSMutableArray *dataArray = [[NSMutableArray alloc] init];
that is all.
就这些。
thank you
谢谢你
回答by Christopher Pickslay
Synchronizing Core Data with Railsis a detailed presentation that includes sample code for serializing/deserializing your Core Data objects to/from JSON (skip to slide 55 for the Core Data part). His sample code assumes a fairly simple model without relationships, though I think it would be pretty easy to extend.
将 Core Data 与 Rails 同步是一个详细的演示文稿,其中包括用于将 Core Data 对象序列化/反序列化为 JSON 的示例代码(对于 Core Data 部分,请跳至幻灯片 55)。他的示例代码假设了一个没有关系的相当简单的模型,但我认为它很容易扩展。
The presentation also goes into some detail about keeping your Core Data model in sync with a REST-based web application, with pointers to some useful libraries, including ObjectiveResourceand ASIHTTPRequest. Not sure if that's what you're trying to do, but it's worth a look even for the Core Data code.
该演示文稿还详细介绍了如何使 Core Data 模型与基于 REST 的 Web 应用程序保持同步,并提供指向一些有用库(包括ObjectiveResource和ASIHTTPRequest )的指针。不确定这是否是您想要做的,但即使是核心数据代码也值得一看。
回答by joshaidan
If you have an NSDatein your managed object, as mentioned above in one of the comments, you'll have problems serializing the object containing the NSDate. A simple fix is to add a JSONDataRepresentationmethod to NSDateusing objective-c categories.
如果您NSDate的托管对象中有 ,如上面评论之一所述,您将在序列化包含NSDate. 一个简单的解决JSONDataRepresentation方法是添加一种NSDate使用objective-c 类别的方法。
Add these two files to your project:
将这两个文件添加到您的项目中:
NSdate.h:
NSdate.h:
#import <Foundation/Foundation.h>
@interface NSDate (jsondatarepresentation)
- (NSData*) JSONDataRepresentation;
@end
NSDate.m:
NSDate.m:
#import "NSDate.h"
@implementation NSDate (jsondatarepresentation)
- (NSData*) JSONDataRepresentation {
return [[[NSNumber numberWithDouble:[self timeIntervalSince1970]] stringValue] dataUsingEncoding:NSUTF8StringEncoding];
}
@end
回答by Fran?ois Téchené
There is a lib that does the JSON synchronization for you : https://github.com/sixdegrees/lidenbrock
有一个库可以为您进行 JSON 同步:https: //github.com/sixdegrees/lidenbrock
回答by Brandon Schlenker
I came across this post which works very well.
我遇到了这篇文章,效果很好。
http://touchalicious.com/blog/2009/10/25/turn-core-data-models-into-json.html
http://touchalicious.com/blog/2009/10/25/turn-core-data-models-into-json.html
Since this is recursive, many-to-many relationships are going to keep looping through themselves. To avoid this, I added an "isExportable" key to the user info dictionary of the relationships in my Core Data model. You can then check for this key and choose to not loop through relationships without it.
由于这是递归的,因此多对多关系将不断循环。为了避免这种情况,我在核心数据模型中的关系的用户信息字典中添加了一个“isExportable”键。然后,您可以检查此键并选择在没有它的情况下不遍历关系。


if ([property isKindOfClass:[NSRelationshipDescription class]])
{
NSRelationshipDescription *relationshipDescription = (NSRelationshipDescription *)property;
if ([[[relationshipDescription userInfo] objectForKey:@"isExportable"] boolValue] == YES)
{
NSString *name = [relationshipDescription name];
if ([relationshipDescription isToMany])
{
NSMutableArray *arr = [properties valueForKey:name];
if (!arr)
{
arr = [[NSMutableArray alloc] init];
[properties setValue:arr forKey:name];
}
for (NSManagedObject *o in [self mutableSetValueForKey:name])
{
[arr addObject:[o propertiesDictionary]];
}
}
else
{
NSManagedObject *o = [self valueForKey:name];
[properties setValue:[o propertiesDictionary] forKey:name];
}
}
}
}
回答by Carl Taylor
Just thought id post a quick update to this question. I followed the Answers by Marcus and Brandon and came up with this for JSON exporting (it uses TouchJSON still):
只是想 id 发布了这个问题的快速更新。我遵循了 Marcus 和 Brandon 的答案,并提出了用于 JSON 导出的方法(它仍然使用 TouchJSON):
- (NSData*)jsonStructureFromManagedObjects:(NSArray*)managedObjects
{
NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
NSData *jsonData = [[CJSONSerializer serializer] serializeArray:objectsArray error:nil];
return jsonData;
}
- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
for (NSManagedObject *managedObject in managedObjects) {
[dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
}
return dataArray;
}
- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject*)managedObject
{
NSDictionary *attributesByName = [[managedObject entity] attributesByName];
NSDictionary *relationshipsByName = [[managedObject entity] relationshipsByName];
NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
[valuesDictionary setObject:[[managedObject entity] name] forKey:@"ManagedObjectName"];
for (NSString *relationshipName in [relationshipsByName allKeys]) {
NSRelationshipDescription *description = [[[managedObject entity] relationshipsByName] objectForKey:relationshipName];
if ([[[description userInfo] objectForKey:@"isExportable"] boolValue] == YES) {
if (![description isToMany]) {
NSManagedObject *relationshipObject = [managedObject valueForKey:relationshipName];
if (relationshipObject) {
[valuesDictionary setObject:[self dataStructureFromManagedObject:relationshipObject] forKey:relationshipName];
}
continue;
}
NSSet *relationshipObjects = [managedObject valueForKey:relationshipName];
NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];
for (NSManagedObject *relationshipObject in relationshipObjects) {
[relationshipArray addObject:[self dataStructureFromManagedObject:relationshipObject]];
}
[valuesDictionary setObject:relationshipArray forKey:relationshipName];
}
}
return valuesDictionary;
}
I couldn't get the import working, maybe that has something to do with the fact that I'm using Magical Record I'm not sure, so Im just looping through the incoming JSON stream and creating objects manually...
我无法使导入工作,也许这与我正在使用 Magical Record 的事实有关我不确定,所以我只是遍历传入的 JSON 流并手动创建对象...
回答by MPajak
Marcus S. Zarra has inspired me to bring the recursive idea to a working version. In this version you don't need to set a key in CoreData and you can cut and paste it in your project :-)
Marcus S. Zarra 启发了我将递归思想带入工作版本。在这个版本中,你不需要在 CoreData 中设置一个键,你可以将它剪切并粘贴到你的项目中:-)
// MARK: - encoding and decoding CoreData entity to dictionary
func dataStructureFromManagedObject( managedObject:NSManagedObject?, parentEntity: NSEntityDescription? = nil) -> NSMutableDictionary {
if (managedObject != nil) {
var attributesByName: NSDictionary = managedObject!.entity.attributesByName
var relationshipsByName: NSDictionary = managedObject!.entity.relationshipsByName
var valuesImmutableDictionary: NSDictionary = managedObject!.dictionaryWithValuesForKeys( attributesByName.allKeys)
var valuesDictionary: NSMutableDictionary = valuesImmutableDictionary.mutableCopy() as NSMutableDictionary
valuesDictionary.setObject( managedObject!.entity.name!, forKey: "ManagedObjectName")
for relationshipNameObject in relationshipsByName.allKeys {
var relationshipName: NSString = relationshipNameObject as NSString
var relationshipDescription: NSRelationshipDescription? = relationshipsByName.objectForKey( relationshipName) as? NSRelationshipDescription
if !relationshipDescription!.toMany {
// ono to one
if parentEntity == nil || (relationshipDescription! as NSRelationshipDescription).destinationEntity != parentEntity! {
// no parent or relationship is "downward" -> object for relationship must be added
var relationshipObject: NSManagedObject? = managedObject!.valueForKey( relationshipName) as? NSManagedObject
var relationshipObjectDictionary: NSMutableDictionary = self.dataStructureFromManagedObject( relationshipObject, parentEntity: managedObject?.entity)
valuesDictionary.setObject( relationshipObjectDictionary, forKey: relationshipName)
} else {
// relationship is "upward" -> nothing to do
}
} else {
// one to many -> all objects must be added
var relationshipObjects: NSSet = managedObject!.mutableSetValueForKey( relationshipName)
var relationshipArray:NSMutableArray = []
for relationshipObjectRaw in relationshipObjects {
var relationshipObject:NSManagedObject? = relationshipObjectRaw as? NSManagedObject
if relationshipObject != nil && !relationshipObject!.entity.isKindOfEntity( managedObject!.entity) {
relationshipArray.addObject(self.dataStructureFromManagedObject( relationshipObject, parentEntity: managedObject?.entity))
}
}
valuesDictionary.setObject( relationshipArray, forKey: relationshipName)
}
}
return valuesDictionary
} else {
return NSMutableDictionary()
}
}
func managedObjectFromStructure( structureDictionary: NSDictionary, moc: NSManagedObjectContext, parentObject: NSManagedObject? = nil) -> NSManagedObject {
if structureDictionary.count > 0 {
var objectName:NSString = structureDictionary.objectForKey( "ManagedObjectName") as NSString
var managedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName( objectName, inManagedObjectContext: moc) as NSManagedObject
var relationshipsByName: NSDictionary = managedObject.entity.relationshipsByName
var realObjectStructure:NSMutableDictionary = structureDictionary.mutableCopy() as NSMutableDictionary
realObjectStructure.removeObjectForKey( "ManagedObjectName")
for key in realObjectStructure.allKeys {
// search for "ManagedObjectName" relationship entrys and delete them before filling the managedObject from this structure
for relationshipName in relationshipsByName.allKeys {
if relationshipName as NSString == key as NSString {
realObjectStructure.removeObjectForKey( key)
}
}
}
managedObject.setValuesForKeysWithDictionary( realObjectStructure)
// the main object with attributes is created. Now care about the relationships
for relationshipName in managedObject.entity.relationshipsByName.keys {
var description:NSRelationshipDescription = relationshipsByName.objectForKey( relationshipName) as NSRelationshipDescription
if !description.toMany {
// to one relationship
if parentObject == nil || description.destinationEntity != parentObject!.entity {
// no parent or relationship is "downward" -> recurse structure to add
var childStructureDictionary:NSDictionary = structureDictionary.objectForKey( relationshipName) as NSDictionary
if childStructureDictionary.count > 0 {
// dictionary not empty -> object must be created and added
var childObject:NSManagedObject? = self.managedObjectFromStructure( childStructureDictionary, moc: moc, parentObject: managedObject)
// validateForUpdate
var error:NSError?
if !managedObject.validateForUpdate( &error) {
println("Error: Object not in valid state for update!!! -> \(error)")
} else {
managedObject.setValue( childObject, forKey: relationshipName as NSString)
}
} else {
// relationship is "upward" -> nothing to do
}
}
} else {
// to many relationship
var relationshipSet:NSMutableSet = managedObject.mutableSetValueForKey( relationshipName as NSString)
var relationshipArray:NSArray = structureDictionary.objectForKey( relationshipName as NSString) as NSArray
for childStructureDictionary in relationshipArray {
if childStructureDictionary.count > 0 {
// dictionary not empty -> object must be created and added
var childObject:NSManagedObject = self.managedObjectFromStructure( childStructureDictionary as NSDictionary, moc: moc, parentObject: managedObject)
// validateForUpdate
var error:NSError?
if !managedObject.validateForUpdate( &error) {
println( "Error: Object not in valid state for update!!! -> \(error)")
} else {
relationshipSet.addObject( childObject)
}
} else {
// no object was behind the relationship -> nothing to do
}
}
// save set
managedObject.setValue( relationshipSet, forKey: relationshipName as NSString)
}
}
// final check validateForUpdate
var error:NSError?
if !managedObject.validateForUpdate( &error) {
println( "Error: Object not in valid state for update although all previous check are passed!!! -> \(error)")
}
return managedObject
} else {
println( "Error: structure for object was empty. this should not happen at this point")
var objectName:NSString = structureDictionary.objectForKey( "ManagedObjectName") as NSString
var managedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName( objectName, inManagedObjectContext: moc) as NSManagedObject
return managedObject
}
}
func dataStructuresFromManagedObjects( managedObjects: NSArray) -> NSArray {
var dataArray:NSMutableArray = []
for managedObject in managedObjects {
dataArray.addObject( self.dataStructureFromManagedObject(managedObject as? NSManagedObject))
}
return dataArray
}
The key here is to pass the parent entity as argument to the recursion, so we can decide which relationship we have to fill with data. So the both functions: dataStructureFromManagedObjectand managedObjectFromStructurecan encode and decode any entity object from CoreData into a dictionary and back into an object.
这里的关键是将父实体作为参数传递给递归,因此我们可以决定必须用数据填充哪个关系。所以这两个功能:dataStructureFromManagedObject并且managedObjectFromStructure可以将 CoreData 中的任何实体对象编码和解码到字典中,然后再返回到对象中。

