【问题标题】:iOS-Swift : Serialize a NSManagedObject to JSON (with relationships)iOS-Swift:将 NSManagedObject 序列化为 JSON(带有关系)
【发布时间】:2015-10-18 18:54:14
【问题描述】:

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

我有这样的 NSManaged 对象:

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 并将其发布到服务器。

但是,我在序列化我的对象时遇到了一个错误,我相信这是因为我的对象不尊重这一点(来自 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)

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

【问题讨论】:

  • 你看过第 3 方框架,比如 RestKit 吗?
  • 使用 fetchRequest.resultType = NSFetchRequestResultType.DictionaryResultType 从 coredata 中获取字典中的数据,然后序列化到 json 中。
  • 对于 JSON 映射器来说是不是太多了? + 我希望尽可能使用 swift
  • @Shoaib :感谢您的回答。为了确保我理解它:如果我想从头开始创建一个 Credential 对象:我创建一个 Credential 和一个成员实体并将它们插入到我的托管对象上下文中,设置它们的成员,然后首先将成员作为字典获取?你下一步怎么做 ?我应该如何将我的会员的字典表示“关联”到我的凭证?

标签: ios json swift core-data nsjsonserialization


【解决方案1】:

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

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

【讨论】:

  • 谢谢,我试过你的解析器,但我在这一行有一个 EXC_BAD_ACCESS 错误代码 = 2:让属性:[字符串] = (entity.propertiesByName as NSDictionary).allKeys as! [字符串];
  • 谢谢,效果更好,但是输出数组不完整,是不是因为我没有提供每个值?结果如下: dataInArr ( { credentialCountCheckIn = 0; credentialCountCheckOut = 0; credentialExtensionFields = ( ); credentialHosts = ( ); credentialId = 1; credentialNumberOfPeople = 0; credentialRSVP = 0; } )
  • 是的。 Null 值不会成为字典的一部分。
  • 谢谢!实际上,如果未指定值,我确实需要我的所有键,事件。所以你的建议是我应该确保我的所有字段(假设都是文本字段)都被填充,检索它们的值,如果我是对的,最后将我的托管对象转换为字典?
【解决方案2】:

您可以使用GrootSync 等第三方框架来实现此目的。

【讨论】:

    【解决方案3】:

    我已经修改了@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
    

    【讨论】:

      【解决方案4】:

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

      func URIRepresentation() -> NSURL
      

      来自 NSManagedObjectID 类?一旦你有了一个 URL,你就可以把它变成一个字符串来放入你的 JSON 对象,这意味着它会正确地序列化。通过围绕这些键的适当结构,可以导出关系。

      当你想重构 ObjectID 时,你可以使用

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

      来自您的 NSPersistentStoreCoordinator 实例。

      【讨论】:

        【解决方案5】:

        我将@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
            }
        }
        

        【讨论】:

        • 日期类型抛出异常。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-02-04
        • 2012-05-31
        • 1970-01-01
        • 1970-01-01
        • 2015-10-06
        • 1970-01-01
        • 2019-07-08
        相关资源
        最近更新 更多