首页 文章

iPhone上的JSON和核心数据

提问于
浏览
92

我有一个核心数据对象图(由两个由多对多关系链接的实体组成) .

我很好奇,作为一个相对缺乏经验的iPhone开发人员,是否有人可以推荐一种方法,以及适合iPhone的JSON实现,这将允许我:

  • 将核心数据记录转换为JSON字符串(同时保持实体之间的关系);和

  • 将JSON字符串转换回核心数据对象(再次保留实体之间的关系) .

我已经在这一点上搜索了一个教程/代码示例,但没有成功,所以我将非常感激地收到任何帮助 .

8 回答

  • 102

    首先,选择一个JSON库来使用,我个人喜欢TouchJSON,但其他几个也很不错 . 复杂的部分虽然不是很难,但是要将您的托管对象转换为适合转换的结构 . 我写得这么快,所以它可能有一两个错误:)

    你调用的方法是:

    - (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects;
    - (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc;
    

    实施如下:

    - (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];
    }
    

    现在这是递归的,所以如果你不小心的话,你可以轻松地翻译整个持久性商店 . 观察您的关系并确保它们仅“关闭”对象树,以便您只获取要翻译的对象 .

  • 2

    我只是想指出一个小错字,导致代码崩溃,希望这会节省你几分钟 .

    - (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects {
    
        NSMutableArray *dataArray = [[NSArray alloc] init];
        for (NSManagedObject *managedObject in managedObjects) {
            [dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
        }
        return [dataArray autorelease];
    }
    

    NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray

    真的应该 NSMutableArray *dataArray = [[NSMutableArray alloc] init];

    就这些 .

    谢谢

  • 9

    Synchronizing Core Data with Rails是一个详细的演示文稿,其中包含用于将核心数据对象序列化/反序列化为JSON的示例代码(跳至核心数据部分的幻灯片55) . 他的示例代码假设一个没有关系的相当简单的模型,但我认为它很容易扩展 .

    该演示文稿还详细介绍了如何使您的Core Data模型与基于REST的Web应用程序保持同步,并指向一些有用的库,包括ObjectiveResourceASIHTTPRequest . 不确定's what you'是否正在尝试这样做,但即使是核心数据代码也值得一看 .

  • 1

    如果您的托管对象中有 NSDate ,如上面其中一条评论中所述,您在序列化包含 NSDate 的对象时会遇到问题 . 一个简单的解决方法是使用objective-c类别将 JSONDataRepresentation 方法添加到 NSDate .

    将这两个文件添加到项目中:

    NSdate.h:

    #import <Foundation/Foundation.h>
    
    @interface NSDate (jsondatarepresentation) 
    
    - (NSData*) JSONDataRepresentation;
    
    @end
    

    NSDate.m:

    #import "NSDate.h"
    
    @implementation NSDate (jsondatarepresentation)
    
    - (NSData*) JSONDataRepresentation {
        return [[[NSNumber numberWithDouble:[self timeIntervalSince1970]] stringValue] dataUsingEncoding:NSUTF8StringEncoding];
    }
    
    @end
    
  • 11

    有一个lib为您执行JSON同步:https://github.com/sixdegrees/lidenbrock

  • 2

    我发现这篇文章非常有效 .

    http://touchalicious.com/blog/2009/10/25/turn-core-data-models-into-json.html

    由于这是递归的,因此多对多关系将继续循环 . 为了避免这种情况,我在核心数据模型中的关系的用户信息字典中添加了一个“isExportable”键 . 然后,您可以检查此密钥,并选择不在没有它的情况下循环关系 .

    enter image description here

    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];
                }
            }
        }
    }
    
  • 7

    只是想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;
    }
    

    我无法让导入工作,也许这与我使用魔法记录的事实有关,我不确定,所以我只是循环传入传入的JSON流并手动创建对象...

  • 2

    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
    }
    

    这里的关键是将父实体作为参数传递给递归,因此我们可以决定用数据填充哪个关系 . 因此,这两个函数: dataStructureFromManagedObjectmanagedObjectFromStructure 可以将CoreData中的任何实体对象编码并解码为字典并返回到对象中 .

相关问题