iOS-Swift: Serialize a NSManagedObject a JSON (con relaciones)

Después de pasar demasiado time tratando de encontrar las mejores prácticas, aquí estoy una vez más pidiendo ayuda, esperando que no sea la única que está luchando con esto:

Tengo objects NSManaged como estos:

import Foundation import CoreData class Cnetworkingential: NSManagedObject { @NSManaged var cnetworkingentialArrivalDate: String? @NSManaged var cnetworkingentialBarCode: String? @NSManaged var cnetworkingentialComment: String? @NSManaged var cnetworkingentialCountCheckIn: Int @NSManaged var cnetworkingentialCountCheckOut: Int @NSManaged var cnetworkingentialDepartureDate: String? @NSManaged var cnetworkingentialId: String? @NSManaged var cnetworkingentialIsArrived: Bool @NSManaged var cnetworkingentialIsCheckedOut: Bool @NSManaged var cnetworkingentialIsHost: Bool @NSManaged var cnetworkingentialLastModificationDate: String? @NSManaged var cnetworkingentialMemberControlled: String? @NSManaged var cnetworkingentialNumberOfPeople: Int @NSManaged var cnetworkingentialRSVP: Int @NSManaged var cnetworkingentialTypeId: String? @NSManaged var cnetworkingentialTypeName: String? @NSManaged var cnetworkingentialZoneId: String? @NSManaged var cnetworkingentialZoneName: String? @NSManaged var cnetworkingentialHosts: Set<Host>? @NSManaged var cnetworkingentialMember: Member @NSManaged var cnetworkingentialExtensionFields: Set<ExtensionFields>? @NSManaged var cnetworkingentialDeltaFirstCheckIn: String? @NSManaged var cnetworkingentialDeltaFirstCheckOut: String? @NSManaged var cnetworkingentialIsCheckedIn: Bool } 

Tengo una relación con una Entidad Miembro:

 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 memberCnetworkingential: Cnetworkingential } 

Lo que trato de hacer es serializar mi object de cnetworkingencial a JSON utilizando NSJSONSerialization.dataWithJSONObject para generar mi JSON y enviarlo a un server.

Sin embargo, tengo un error al serializar mi object, y creo que esto se debe a que mi object no respeta esto (de la documentation de Apple):

Un object que se puede convertir a JSON debe tener las siguientes properties:

  • El object de nivel superior es un NSArray o NSDictionary.
  • Todos los objects son instancias de NSString, NSNumber, NSArray, NSDictionary o NSNull.
  • Todas las keys del dictionary son instancias de NSString.
  • Los numbers no son NaN o infinito.

Entonces, ¿cómo puedo serializar mi object a JSON? ¿Debería encontrar alguna forma de convertir un miembro en un dictionary, luego tratar de encontrar una manera de establecer este dictionary como el valor de mi relación con una cnetworkingencial?

¿He perdido un mapeador incorporado que lo maneja?

Lo siento si eso no está claro pero cualquier ayuda sería muy agradable.

De antemano, gracias.

Hugo

EDITAR :

Como se sugirió en una respuesta, traté de search un dictionary en lugar de un NSManagedObject pero el dictionary registrado está vacío (sin embargo, el mismo código funcionó para una class muy simple con algunos attributes de cadena y sin relación), aquí está el código:

 /******************************* TEST : Fetch dictionary instead of NSManagedObject */ // create Cnetworkingential entity let newCnetworkingential = NSEntityDescription.insertNewObjectForEntityForName("Cnetworkingential", inManagedObjectContext: self.managedObjectContext!) as! Cnetworkingential // create Cnetworkingential entity let newMember = NSEntityDescription.insertNewObjectForEntityForName("Member", inManagedObjectContext: self.managedObjectContext!) as! Member // instanciate some fields newCnetworkingential.cnetworkingentialId = "44444" newMember.memberFirstName = "TEST" // set the relationship newCnetworkingential.cnetworkingentialMember = newMember // retrieve currentCnetworkingential let requestGuest = NSFetchRequest(entityName: "Cnetworkingential") // 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) /*******************************/ 

Intenté lo mismo, pero no pude tener éxito con las relaciones uno a uno y uno a muchos. Entonces escribí un analizador para convertir objects administrados en dictionary y matriz. Puedes usarlo;

 //Tested on xCode 6.4 - Swift 1.2 let requestGuest = NSFetchRequest(entityName: "Cnetworkingential") 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.OrdenetworkingSame) { 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 

He revisado la respuesta de @Shoaib porque en CoreData tuve una relación bidireccional que causó este bucle infinito. Ahora esto evita procesar el mismo NSManagedObject dos veces utilizando un set de objects.

 // // 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.OrdenetworkingSame) { let value: AnyObject? = managedObject.valueForKey(property); //-- if let set:NSSet = value as? NSSet { rtnDict[property] = self.convertToArray(set.allObjects, parentNode: parentNode); } else if let ordenetworkingset:NSOrdenetworkingSet = value as? NSOrdenetworkingSet { rtnDict[property] = self.convertToArray(NSArray(array: ordenetworkingset.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 

Puede lograr esto utilizando un marco de terceros como Groot o Sync .

Con el paso del time, esta respuesta puede estar desactualizada, pero ¿ha explorado el uso de

 func URIRepresentation() -> NSURL 

de la class NSManagedObjectID? Una vez que tenga una URL, puede convertirla en una Cadena para ponerla en su Objeto JSON, lo que significa que se serializará correctamente. Con la estructura apropiada alnetworkingedor de estas keys, las relaciones se pueden exportar.

Cuando desee reconstituir el ObjectID, entonces usa el

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

desde su instancia de NSPersistentStoreCoordinator.