iOS-Swift: Сериализовать NSManagedObject для JSON (с отношениями)

Проведя слишком много времени, пытаясь найти лучшие практики, здесь я снова прошу о помощи, надеясь, что я не единственный, кто борется с этим:

У меня есть NSManaged Objects, подобные этим:

import Foundation import CoreData class Credential: NSManagedObject { @NSManaged var credentialArrivalDate: String? @NSManaged var credentialBarCode: String? @NSManaged var credentialComment: String? @NSManaged var credentialCountCheckIn: Int @NSManaged var credentialCountCheckOut: Int @NSManaged var credentialDepartureDate: String? @NSManaged var credentialId: String? @NSManaged var credentialIsArrived: Bool @NSManaged var credentialIsCheckedOut: Bool @NSManaged var credentialIsHost: Bool @NSManaged var credentialLastModificationDate: String? @NSManaged var credentialMemberControlled: String? @NSManaged var credentialNumberOfPeople: Int @NSManaged var credentialRSVP: Int @NSManaged var credentialTypeId: String? @NSManaged var credentialTypeName: String? @NSManaged var credentialZoneId: String? @NSManaged var credentialZoneName: String? @NSManaged var credentialHosts: Set<Host>? @NSManaged var credentialMember: Member @NSManaged var credentialExtensionFields: Set<ExtensionFields>? @NSManaged var credentialDeltaFirstCheckIn: String? @NSManaged var credentialDeltaFirstCheckOut: String? @NSManaged var credentialIsCheckedIn: Bool } 

У меня есть отношения с Entity Entity:

 import Foundation import CoreData class Member: NSManagedObject { @NSManaged var memberCompany: String @NSManaged var memberEMail: String @NSManaged var memberFirstName: String @NSManaged var memberId: String @NSManaged var memberJob: String @NSManaged var memberLastModificationDate: String @NSManaged var memberLastName: String @NSManaged var memberPhoneNumber: String @NSManaged var memberTitle: String @NSManaged var memberCredential: Credential } 

Я пытаюсь сделать сериализацию моего объекта Credential для JSON с помощью NSJSONSerialization.dataWithJSONObject для генерации моего JSON и POST его на сервер.

Тем не менее, у меня есть ошибка при сериализации моего объекта, и я считаю, что это потому, что мой объект не соблюдает это (из документации Apple):

Объект, который может быть преобразован в JSON, должен иметь следующие свойства:

  • Объект верхнего уровня – NSArray или NSDictionary.
  • Все объекты являются экземплярами NSString, NSNumber, NSArray, NSDictionary или NSNull.
  • Все словарные ключи являются экземплярами NSString.
  • Числа не NaN или бесконечность.

Итак, как я могу сериализовать свой объект JSON? Должен ли я найти способ преобразования члена в словарь, а затем попытаться найти способ установить этот словарь в качестве значения для моих отношений с учетными данными?

Пропустил ли я встроенный картограф, который его обрабатывает?

Прошу прощения, если это не ясно, но любая помощь будет очень приятной.

Заранее благодарю вас.

Хьюго

EDIT :

Как было предложено в ответе, я попытался извлечь словарь вместо NSManagedObject, но зарегистрированный словарь пуст (однако, тот же код работал для очень простого класса с несколькими строковыми атрибутами и без отношения), вот код:

 /******************************* TEST : Fetch dictionary instead of NSManagedObject */ // create Credential entity let newCredential = NSEntityDescription.insertNewObjectForEntityForName("Credential", inManagedObjectContext: self.managedObjectContext!) as! Credential // create Credential entity let newMember = NSEntityDescription.insertNewObjectForEntityForName("Member", inManagedObjectContext: self.managedObjectContext!) as! Member // instanciate some fields newCredential.credentialId = "44444" newMember.memberFirstName = "TEST" // set the relationship newCredential.credentialMember = newMember // retrieve currentCredential let requestGuest = NSFetchRequest(entityName: "Credential") // get a dictionary instead of NSManagedObject requestGuest.resultType = NSFetchRequestResultType.DictionaryResultType let fetchedGuest = (managedObjectContext!.executeFetchRequest(requestGuest, error:nil))! // no need to cast here ? println(fetchedGuest.description) /*******************************/ 

Я пробовал то же самое, но не смог добиться успеха в отношениях один к одному и от одного до многих. Поэтому я написал парсер для преобразования управляемых объектов в словарь и массив. Вы можете использовать его;

 //Tested on xCode 6.4 - Swift 1.2 let requestGuest = NSFetchRequest(entityName: "Credential") let fetchedGuest = (managedObjectContext!.executeFetchRequest(requestGuest, error:nil))! let dataInArr:NSArray = ManagedParser.convertToArray(fetchedGuest); NSLog("dataInArr \(dataInArr)"); 

// –

 // // ManagedParser.swift // Created by Shoaib on 7/29/15. // import UIKit import CoreData class ManagedParser: NSObject { class func convertToArray(managedObjects:NSArray?, parentNode:String? = nil) -> NSArray { var rtnArr:NSMutableArray = NSMutableArray(); //-- if let managedObjs:[NSManagedObject] = managedObjects as? [NSManagedObject] { for managedObject:NSManagedObject in managedObjs { rtnArr.addObject(self.convertToDictionary(managedObject, parentNode: parentNode)); } } return rtnArr; } //FE class func convertToDictionary(managedObject:NSManagedObject, parentNode:String? = nil) -> NSDictionary { var rtnDict:NSMutableDictionary = NSMutableDictionary(); //- let entity:NSEntityDescription = managedObject.entity; let properties:[String] = (entity.propertiesByName as NSDictionary).allKeys as! [String]; //-- let parentNode:String = parentNode ?? managedObject.entity.name!; for property:String in properties { if (property.caseInsensitiveCompare(parentNode) != NSComparisonResult.OrderedSame) { let value: AnyObject? = managedObject.valueForKey(property); //-- if let set:NSSet = value as? NSSet { rtnDict[property] = self.convertToArray(set.allObjects, parentNode: parentNode); } else if let vManagedObject:NSManagedObject = value as? NSManagedObject { if (vManagedObject.entity.name != parentNode) { rtnDict[property] = self.convertToDictionary(vManagedObject, parentNode: parentNode); } } else if let vData:AnyObject = value { rtnDict[property] = vData; } } } return rtnDict; } //FE } //CLS END 

Я пересмотрел ответ @Shoaib, потому что в CoreData у меня было двухстороннее отношение, которое вызвало этот бесконечный цикл. Теперь это предотвращает одновременную обработку одного NSManagedObject с помощью набора объектов.

 // // ManagedParser.swift // Created by Shoaib on 7/29/15. // http://stackoverflow.com/questions/31672558/ios-swift-serialize-a-nsmanagedobject-to-json-with-relationships // Mod by dkavraal on 7/28/16 // import CoreData class ManagedParser: NSObject { private var parsedObjs:NSMutableSet = NSMutableSet(); func convertToArray(managedObjects:NSArray?, parentNode:String? = nil) -> NSArray { let rtnArr:NSMutableArray = NSMutableArray(); //-- if let managedObjs:[NSManagedObject] = managedObjects as? [NSManagedObject] { for managedObject:NSManagedObject in managedObjs { if self.parsedObjs.member(managedObject) == nil { self.parsedObjs.addObject(managedObject); rtnArr.addObject(self.convertToDictionary(managedObject, parentNode: parentNode)); } } } return rtnArr; } //FE func convertToDictionary(managedObject:NSManagedObject, parentNode:String? = nil) -> NSDictionary { let rtnDict:NSMutableDictionary = NSMutableDictionary(); //- let entity:NSEntityDescription = managedObject.entity; let properties:[String] = (entity.propertiesByName as NSDictionary).allKeys as? [String] ?? []; //-- let parentNode:String = parentNode ?? managedObject.entity.name!; for property:String in properties { if (property.caseInsensitiveCompare(parentNode) != NSComparisonResult.OrderedSame) { let value: AnyObject? = managedObject.valueForKey(property); //-- if let set:NSSet = value as? NSSet { rtnDict[property] = self.convertToArray(set.allObjects, parentNode: parentNode); } else if let orderedset:NSOrderedSet = value as? NSOrderedSet { rtnDict[property] = self.convertToArray(NSArray(array: orderedset.array), parentNode: parentNode); } else if let vManagedObject:NSManagedObject = value as? NSManagedObject { if self.parsedObjs.member(managedObject) == nil { self.parsedObjs.addObject(managedObject); if (vManagedObject.entity.name != parentNode) { rtnDict[property] = self.convertToDictionary(vManagedObject, parentNode: parentNode); } } } else if let vData:AnyObject = value { rtnDict[property] = vData; } } } return rtnDict; } //FE } //CLS END 

Вы можете достичь этого, используя стороннюю структуру, такую ​​как Groot или Sync .

С течением времени этот ответ может быть устаревшим, но вы изучили использование

 func URIRepresentation() -> NSURL 

из класса NSManagedObjectID? После того, как у вас есть URL-адрес, вы можете превратить его в String, чтобы поместить в свой JSON-объект, что означает, что он будет сериализоваться должным образом. При соответствующей структуре вокруг этих ключей, отношения могут быть экспортированы.

Когда вы хотите восстановить ObjectID, вы затем используете

 func managedObjectIDForURIRepresentation(_ url: NSURL) -> NSManagedObjectID? 

из вашего экземпляра NSPsistentStoreCoordinator.

Interesting Posts
Давайте будем гением компьютера.