JSON и core data на iPhone

У меня есть базовый граф объектов данных (состоящий из двух объектов, связанных отношением to-many).

Мне было любопытно, как относительно неопытный разработчик iPhone, может ли кто-нибудь рекомендовать подход и подходящую реализацию JSON для iPhone, что позволило бы мне:

  1. конвертировать записи данных ядра в строку JSON (при сохранении взаимосвязи между объектами); а также

  2. преобразовать строку JSON обратно в основные объекты данных (снова сохраняя связь между объектами).

Я искал, безуспешно, для образца учебника / кода по этому вопросу, поэтому любая помощь была бы с благодарностью получена.

Во-первых, выберите библиотеку 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]; } 

Теперь это рекурсивно, поэтому вы можете легко перевести весь свой постоянный магазин, если не будете осторожны. Следите за своими отношениями и убедитесь, что они только «опускают» дерево объектов, так что вы получаете только те объекты, которые хотите перевести.

Я просто хотел указать небольшую опечатку, которая вызвала сбой кода, и, надеюсь, это сэкономит вам несколько минут.

 - (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 = [[NSArray alloc] init]; // This should be NSMutableArray

действительно должен быть NSMutableArray *dataArray = [[NSMutableArray alloc] init];

вот и все.

Спасибо

Синхронизация основных данных с Rails – это подробный отчет, который включает пример кода для сериализации / десериализации объектов Core Data в / из JSON (пропустите слайд 55 для части Core Data). Его пример кода предполагает довольно простую модель без отношений, хотя я думаю, что ее было бы довольно легко расширить.

В презентации также подробно описывается сохранение вашей модели Core Data в синхронизации с веб-приложением на основе REST с указателями на некоторые полезные библиотеки, включая ObjectiveResource и ASIHTTPRequest . Не уверен, что это то, что вы пытаетесь сделать, но это стоит посмотреть даже на код Core Data.

Если у вас есть NSDate в управляемом объекте, как упоминалось выше в одном из комментариев, у вас возникнут проблемы с сериализацией объекта, содержащего NSDate . Простое исправление заключается в добавлении метода NSDate в NSDate с использованием категорий объектов-c.

Добавьте эти два файла в свой проект:

NSdate.h:

 #import  @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 

Существует lib, который выполняет синхронизацию JSON для вас: https://github.com/sixdegrees/lidenbrock

Я наткнулся на этот пост, который работает очень хорошо.

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

Поскольку это рекурсивно, отношения «многие ко многим» собираются продолжать цикл. Чтобы этого избежать, я добавил ключ «isExportable» в словарь пользовательской информации о взаимоотношениях в моей модели Core Data. Затем вы можете проверить этот ключ и выбрать, чтобы он не пересекал отношения без него.

введите описание изображения здесь

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

Просто подумал, что я вернусь к этому вопросу быстро. Я следил за ответами Маркуса и Брэндона и придумал это для экспорта 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; } 

Я не мог заставить импорт работать, возможно, это связано с тем, что я использую Magical Record. Я не уверен, поэтому я просто перебираю входящий stream JSON и создаю объекты вручную …

Маркус С. Зарра вдохновил меня привести рекурсивную идею к рабочей версии. В этой версии вам не нужно устанавливать ключ в 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 } 

Ключевым моментом здесь является передача родительского объекта в качестве аргумента в рекурсию, поэтому мы можем решить, какое отношение мы должны заполнить данными. Таким образом, обе функции: dataStructureFromManagedObject и managedObjectFromStructure могут кодировать и декодировать любой объект объекта из CoreData в словарь и обратно в объект.

  • Верно ли, что NS64 () не должен использовать производственный код?
  • Разделить NSString для доступа к одной конкретной части
  • Декодирование символов UTF8 в Objective C
  • Можно ли отделить основную функцию и classы C ++ от подпрограмм Objective-C и / или C при компиляции и ссылке?
  • Как декодировать поле полилиний API Google Directions в длинные точки lat в объективе C для iPhone?
  • Клиентские / серверные GKSessions
  • "F" после числа / float в Objective-C / C
  • iOS 7 sizeWithAttributes: замена для sizeWithFont: constrainedToSize
  • Заменить несколько символов в строке в Objective-C?
  • NSInvocation для чайников?
  • NSString tokenize в Objective-C
  • Давайте будем гением компьютера.