iOS-Swift:将NSManagedObject序列化为JSON(带关系)

时间:2015-07-28 09:36:09

标签: ios json swift core-data nsjsonserialization

在花了太多时间试图找到最佳实践之后,我再次寻求一些帮助,希望我不是唯一一个在这方面苦苦挣扎的人:

我有这样的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

}

我与会员实体有关系:

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

}

我要做的是使用NSJSONSerialization.dataWithJSONObject将我的Credential对象序列化为JSON,以生成我的JSON并将其POST到服务器。

但是,我在序列化对象时遇到错误,我相信这是因为我的对象不尊重这个(来自Apple文档):

可以转换为JSON的对象必须具有以下属性:

  • 顶级对象是NSArray或NSDictionary。
  • 所有对象都是NSString,NSNumber,NSArray,NSDictionary或NSNull的实例。
  • 所有字典键都是NSString的实例。
  • 数字不是NaN或无穷大。

那么如何将我的对象序列化为JSON?我是否应该找到将会员转换为字典的方法,然后尝试找到一种方法将此字典设置为我与凭证的关系的值?

我是否错过了处理它的内置映射器?

如果不清楚,我很抱歉,但任何帮助都会非常好。

事先,谢谢。

雨果

编辑

正如在答案中所建议的,我试图获取字典而不是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)

    /*******************************/

5 个答案:

答案 0 :(得分:5)

我尝试了同样的但是无法通过一对一和一对多关系获得成功。所以我编写了一个解析器来将托管对象转换为字典和数组。你可以使用它;

//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

答案 1 :(得分:2)

我修改了@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;
    } //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

答案 2 :(得分:1)

您可以使用GrootSync等第三方框架来实现这一目标。

答案 3 :(得分:0)

随着时间的推移,这个答案可能已经过时了,但你是否已经探讨了

的用法
func URIRepresentation() -> NSURL
来自NSManagedObjectID类?获得URL后,可以将其转换为String以放入JSON对象,这意味着它将正确序列化。通过围绕这些键的适当结构,可以导出关系。

如果要重新构建ObjectID,则使用

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

来自您的NSPersistentStoreCoordinator实例。

答案 4 :(得分:0)

我将@JSBach的答案更新为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
    }
}