У меня есть основной граф объектов данных (состоящий из двух сущностей, связанных отношением ко многим).
Мне, как относительно неопытному разработчику iPhone, было любопытно, может ли кто-нибудь порекомендовать подход и подходящую реализацию JSON для iPhone, которая позволила бы мне:
преобразовать основные записи данных в строку JSON (при сохранении связи между сущностями); а также
преобразовать строку JSON обратно в основные объекты данных (снова сохраняя связь между сущностями).
Я безуспешно искал учебник / образец кода по этому вопросу, поэтому любая помощь будет с благодарностью получена.
Для всех, кто интересуется этим для iOS5, теперь есть NSJSONSerialization developer.apple.com/library/mac/#documentation/Foundation/ stackoverflow.com/questions/6726899/nsjsonserialization-in-ios5 nicerobot 03.12.2011
Я знаю, что этот вопрос немного устарел, но я создал простую библиотеку под названием OSReflectionKit, которая позволяет сериализовать / десериализовать объекты в / из JSON, используя NSJSONSerialization или NSDictionary. Он также поддерживает объекты Core Data. Alexandre OS 06.08.2013
Во-первых, выберите для использования библиотеку JSON. Мне лично нравится TouchJSON, но есть и другие. Сложная часть, хотя и не очень сложная, заключается в преобразовании ваших управляемых объектов в подходящие структуры для преобразования. Я написал это очень быстро, поэтому может быть ошибка или две :)
Теперь это рекурсивно, так что вы можете легко в конечном итоге перевести все свое постоянное хранилище, если не будете осторожны. Следите за своими отношениями и убедитесь, что они идут только "вниз" по дереву объектов, чтобы вы получали только те объекты, которые хотите перевести.
Ответ 2 · рейтинг 12 · creativeKoder
Я просто хотел указать на небольшую опечатку, которая привела к сбою кода, и, надеюсь, это сэкономит вам несколько минут.
NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray
действительно должно быть NSMutableArray *dataArray = [[NSMutableArray alloc] init];
это все.
Спасибо
Ответ 3 · рейтинг 10 · Christopher Pickslay
Синхронизация основных данных с Rails - это подробная презентация, которая включает пример кода. для сериализации / десериализации ваших объектов Core Data в / из JSON (перейдите к слайду 55 для части Core Data). Его пример кода предполагает довольно простую модель без взаимосвязей, хотя я думаю, что ее будет довольно легко расширить.
В презентации также подробно рассказывается о том, как поддерживать вашу модель Core Data в синхронизации с веб-приложением на основе REST, с указателями на некоторые полезные библиотеки, включая ObjectiveResource и ASIHTTPRequest. Не уверен, что это то, что вы пытаетесь сделать, но стоит взглянуть даже на код Core Data.
Ответ 4 · рейтинг 7 · joshaidan
Если у вас есть NSDate в вашем управляемом объекте, как упоминалось выше в одном из комментариев, у вас возникнут проблемы с сериализацией объекта, содержащего NSDate. Простое исправление - добавить метод JSONDataRepresentation в NSDate с использованием категорий objective-c.
Просто подумал, что id опубликует быстрое обновление этого вопроса. Я следил за ответами Маркуса и Брэндона и придумал это для экспорта JSON (он все еще использует TouchJSON):
Мне не удалось заставить импорт работать, возможно, это как-то связано с тем, что я использую Magical Record, я не уверен, поэтому я просто перебираю входящий поток JSON и создаю объекты вручную ...
Поскольку это рекурсивно, отношения "многие ко многим" будут продолжать зацикливаться. Чтобы избежать этого, я добавил ключ isExportable в пользовательский информационный словарь отношений в моей модели Core Data. Затем вы можете проверить наличие этого ключа и отказаться от цикла отношений без него.
Маркус С. Зарра вдохновил меня довести рекурсивную идею до рабочей версии. В этой версии вам не нужно устанавливать ключ в 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 в словарь и обратно в объект.