问题
After spending too much time trying to find the best practices, here I am once again asking for some help, hoping I am not the only one 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 relationship with a Member 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 am trying to do is to serialize my Credential object to JSON using NSJSONSerialization.dataWithJSONObject to generate my JSON and POST it to a Server.
However, I've got an error while serializing my object, and I believe this is because my object does not respect this (from Apple documentation) :
An object that may be converted to JSON must have the following properties:
- The top level object is an 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 to JSON ? Should I find some way to convert a Member to a Dictionary, then try to find a way to set this dictionary as the value for my relationship with a credential ?
Have I missed a built-in mapper that handles it ?
I am sorry if that is not clear but any help would be very nice.
In advance, thank you.
Hugo
EDIT :
As suggested in an answer, I tried to fetch a dictionary instead of a NSManagedObject but the logged dictionary is empty (however, the same code worked for a very simple class with a few string attributes and no relationship), 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)
/*******************************/
回答1:
I tried the same but could not get succeed with one to one and one to many relationships. So I wrote a parser to convert managed objects to dictionary and array. You may 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;
} //F.E.
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;
} //F.E.
} //CLS END
回答2:
I have revised answer of @Shoaib because on CoreData I had two way relationship which caused this infinite loop. Now this prevents to process same NSManagedObject twice by using an object set.
//
// 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;
} //F.E.
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;
} //F.E.
} //CLS END
回答3:
You could achieve this using a third-party framework like Groot or Sync.
回答4:
With the passage of time, this answer may be out of date, but have you explored the use of
func URIRepresentation() -> NSURL
from the NSManagedObjectID class? Once you have a URL, you can turn that into a String to put into your JSON Object, which means that it will serialise properly. With the appropriate structure around these keys, then the relationships can be exported.
When you want to reconstitute the ObjectID, you then use the
func managedObjectIDForURIRepresentation(_ url: NSURL) -> NSManagedObjectID?
from your instance of NSPersistentStoreCoordinator.
回答5:
I updated answer of @JSBach to Swift 5.
import CoreData
class ManagedParser {
private var parsedObjs = NSMutableSet()
func convertToArray(managedObjects: NSArray?, parentNode: String? = nil) -> NSArray {
let resultArray = NSMutableArray()
if let managedObjects = managedObjects as? [NSManagedObject] {
for managedObject in managedObjects {
if self.parsedObjs.member(managedObject) == nil {
self.parsedObjs.add(managedObject)
resultArray.add(self.convertToDictionary(managedObject: managedObject,
parentNode: parentNode))
}
}
}
return resultArray
}
func convertToDictionary(managedObject: NSManagedObject, parentNode: String? = nil) -> NSDictionary {
let resultDictonary = NSMutableDictionary()
let entity = managedObject.entity
let properties: [String] = (entity.propertiesByName as NSDictionary).allKeys as? [String] ?? []
let parentNode = parentNode ?? managedObject.entity.name!
for property in properties {
if property.caseInsensitiveCompare(parentNode) != .orderedSame {
let value = managedObject.value(forKey: property)
if let set = value as? NSSet {
resultDictonary[property] = self.convertToArray(managedObjects: set.allObjects as NSArray, parentNode: parentNode)
} else if let orderedset = value as? NSOrderedSet {
resultDictonary[property] = self.convertToArray(managedObjects: NSArray(array: orderedset.array), parentNode: parentNode)
} else if let vManagedObject = value as? NSManagedObject {
if self.parsedObjs.member(managedObject) == nil {
self.parsedObjs.add(managedObject)
if vManagedObject.entity.name != parentNode {
resultDictonary[property] = self.convertToDictionary(managedObject: vManagedObject, parentNode: parentNode)
}
}
} else if let vData = value {
resultDictonary[property] = vData
}
}
}
return resultDictonary
}
}
来源:https://stackoverflow.com/questions/31672558/ios-swift-serialize-a-nsmanagedobject-to-json-with-relationships