IOS-Swift: Serialize NSManagedObject for JSON (with relationships)

Having spent too much time trying to find best practices, here again I ask for help, hoping that I am not the only one who is struggling with this:

I have NSManaged Objects like these:

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 } 

I have a connection with 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 } 

What I'm trying to do is serialize my Credential object for JSON using NSJSONSerialization.dataWithJSONObject to generate my JSON and POST it to the server.

However, I have an error while serializing my object, and I believe that this is because my object does not comply with this (from the Apple documentation):

An object that can be converted to JSON must have the following properties:

  • The top-level object is NSArray or NSDictionary.
  • All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
  • All dictionary keys are instances of NSString.
  • Numbers are not NaN or infinity.

So how can I serialize my object in JSON? Should I find a way to convert a member to a dictionary, and then try to find a way to set this dictionary as a value for my credential relationship?

Did I miss the built-in handler that processes it?

I apologize if this is not clear, but any help would be very pleasant.

Thank you in advance.

Hugo

EDIT :

As suggested in the answer, I tried to extract the dictionary instead of NSManagedObject, but the registered dictionary is empty (however, the same code worked for a very simple class with several string attributes and no relation), here is the code:

 /******************************* 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) /*******************************/ 
+4
source share
4 answers

I tried the same thing, but could not succeed in a one-to-one relationship and from one to many. So I wrote a parser to convert managed objects into a dictionary and an array. You can use it,

 //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 
+4
source

I reworked @Shoaib's answer because on CoreData I had a two-way relationship that caused this infinite loop. This now prevents the processing of the same NSManagedObject twice with a set of 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.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 
+2
source

You can achieve this using a third-party framework like Groot or Sync .

+1
source

This answer may be deprecated over time, but you have studied the use of

 func URIRepresentation() -> NSURL 

from class NSManagedObjectID? Once you have the URL, you can turn it into a String to put in your JSON object, which means it will serialize properly. With an appropriate structure around these keys, relationships can be exported.

If you want to restore the ObjectID, then use

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

from your instance of NSPersistentStoreCoordinator.

0
source

All Articles