iPhone의 JSON 및 핵심 데이터


93

핵심 데이터 개체 그래프가 있습니다 (대다 관계로 연결된 두 항목으로 구성됨).

비교적 경험이없는 iPhone 개발자로서 누구나 접근 방식을 추천 할 수 있는지 여부와 iPhone에 적합한 JSON 구현이 궁금했습니다.

  1. 핵심 데이터 레코드를 JSON 문자열로 변환합니다 (엔티티 간의 관계를 유지하면서). 과

  2. JSON 문자열을 다시 핵심 데이터 객체로 변환합니다 (다시 엔티티 간의 관계 유지).

이 시점에서 튜토리얼 / 코드 샘플을 검색하지 못했지만 모든 도움이 감사하게 받아 들여질 것입니다.



이 질문이 조금 오래되었다는 것을 알고 있지만 NSJSONSerialization 또는 NSDictionary를 사용하여 JSON과 객체를 직렬화 / 역 직렬화 할 수있는 OSReflectionKit 이라는 간단한 라이브러리를 만들었습니다 . 또한 Core Data 개체를 지원합니다.
Alexandre OS

답변:


103

먼저 사용할 JSON 라이브러리를 선택합니다. 저는 개인적으로 TouchJSON을 좋아하지만 다른 몇 가지도 꽤 좋습니다. 그다지 어렵지는 않지만 복잡한 부분은 관리되는 개체를 변환에 적합한 구조로 변환하는 것입니다. 나는 이것을 진짜 빨리 썼으므로 오류가있을 수 있습니다. :)

호출하는 방법은 다음과 같습니다.

- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects;
- (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc;

그리고 구현은 다음과 같습니다.

- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject*)managedObject
{
  NSDictionary *attributesByName = [[managedObject entity] attributesByName];
  NSDictionary *relationshipsByName = [[managedObject entity] relationshipsByName];
  NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
  [valuesDictionary setObject:[[managedObject entity] name] forKey:@"ManagedObjectName"];
  for (NSString *relationshipName in [relationshipsByName allKeys]) {
    NSRelationshipDescription *description = [[[managedObject entity] relationshipsByName] objectForKey:relationshipName];
    if (![description isToMany]) {
      NSManagedObject *relationshipObject = [managedObject valueForKey:relationshipName];
      [valuesDictionary setObject:[self dataStructureForManagedObject:relationshipObject] forKey:relationshipName];
      continue;
    }
    NSSet *relationshipObjects = [managedObject objectForKey:relationshipName];
    NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];
    for (NSManagedObject *relationshipObject in relationshipObjects) {
      [relationshipArray addObject:[self dataStructureForManagedObject:relationshipObject]];
    }
    [valuesDictionary setObject:relationshipArray forKey:relationshipName];
  }
  return [valuesDictionary autorelease];
}

- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects
{
  NSMutableArray *dataArray = [[NSMutableArray alloc] init];
  for (NSManagedObject *managedObject in managedObjects) {
    [dataArray addObject:[self dataStructureForManagedObject:managedObject]];
  }
  return [dataArray autorelease];
}

- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects
{
  NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
  NSString *jsonString = [[CJSONSerializer serializer] serializeArray:objectsArray];
  return jsonString;
}

- (NSManagedObject*)managedObjectFromStructure:(NSDictionary*)structureDictionary withManagedObjectContext:(NSManagedObjectContext*)moc
{
  NSString *objectName = [structureDictionary objectForKey:@"ManagedObjectName"];
  NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:objectName inManagedObjectContext:moc];
  [managedObject setValuesForKeysWithDictionary:structureDictionary];

  for (NSString *relationshipName in [[[managedObject entity] relationshipsByName] allKeys]) {
    NSRelationshipDescription *description = [relationshipsByName objectForKey:relationshipName];
    if (![description isToMany]) {
      NSDictionary *childStructureDictionary = [structureDictionary objectForKey:relationshipName];
      NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
      [managedObject setObject:childObject forKey:relationshipName];
      continue;
    }
    NSMutableSet *relationshipSet = [managedObject mutableSetForKey:relationshipName];
    NSArray *relationshipArray = [structureDictionary objectForKey:relationshipName];
    for (NSDictionary *childStructureDictionary in relationshipArray) {
      NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
      [relationshipSet addObject:childObject];
    }
  }
  return managedObject;
}

- (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc
{
  NSError *error = nil;
  NSArray *structureArray = [[CJSONDeserializer deserializer] deserializeAsArray:json error:&error];
  NSAssert2(error == nil, @"Failed to deserialize\n%@\n%@", [error localizedDescription], json);
  NSMutableArray *objectArray = [[NSMutableArray alloc] init];
  for (NSDictionary *structureDictionary in structureArray) {
    [objectArray addObject:[self managedObjectFromStructure:structureDictionary withManagedObjectContext:moc]];
  }
  return [objectArray autorelease];
}

이제 이것은 재귀 적이므로주의하지 않으면 전체 영구 저장소를 쉽게 번역 할 수 있습니다. 관계를 관찰하고 번역하려는 개체 만 얻을 수 있도록 개체 트리의 "아래"로만 이동하는지 확인합니다.


또 다른 훌륭한 답변과 매우 유용한 책에 다시 한 번 감사드립니다! :)
Urizen

2
안녕 마커스. 방금 위의 코드를 시도해 보았습니다 (컴파일을 위해 약간의 수정을 가하고 앱이 충돌 할 때까지 실행이 무한정 계속되는 것 같습니다). 귀찮게해서 미안하지만이 문제를 해결하기위한 올바른 방향을 알려줄 수 있는지 궁금했습니다. datastructureFromManagedObject 메서드의 재귀에서 발생하는 것 같습니다 ...
Urizen

1
데이터 구조에 따라 다릅니다. 모델이 루프를 생성하면 영원히 실행됩니다. 데이터 모델을 검토하고 트리 디자인인지 확인하거나 루프를 방지하기 위해 재귀 코드에 논리 중지를 넣습니다.
Marcus S. Zarra

1
이 코드를 실제로 실행 해 보셨습니까? 오류가 너무 많습니다. dataStructureForManagedObject도 존재하지 않습니다. 오타 일 뿐이라고 생각했지만 dataStructureFromManagedObject로 변경하면 모든 것이 관계 쌍 사이에서 무한히 바운딩됩니다. 여기에 추가 코드가 누락 되었습니까?
Chris Mitchelmore

1
이 코드 예제는 2 년 전에 브라우저에서 작성되었습니다. 복사 및 붙여 넣기가 아닌 영감을주기위한 것입니다. 무한 루프의 경우, 이는 모델에 루프가 있음을 의미하며 그런 다음주기를 끊기 위해 앱에 모델 별 로직을 추가해야합니다. 이 예제에없는 여러 가지 방법이 있습니다.
Marcus S. Zarra 2011

12

코드 충돌을 일으킨 작은 오타를 지적하고 싶었습니다. 이로 인해 몇 분이 절약되기를 바랍니다.

- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects {

    NSMutableArray *dataArray = [[NSArray alloc] init];
    for (NSManagedObject *managedObject in managedObjects) {
        [dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
    }
    return [dataArray autorelease];
}

그만큼 NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray

정말이어야합니다 NSMutableArray *dataArray = [[NSMutableArray alloc] init];

그게 다입니다.

감사합니다


10

Core Data와 Rails의 동기화 는 JSON에서 Core Data 객체를 직렬화 / 역 직렬화하기위한 샘플 코드가 포함 된 자세한 프레젠테이션입니다 (Core Data 부분은 슬라이드 55로 건너 뛰기). 그의 샘플 코드는 확장하기가 꽤 쉬울 것이라고 생각하지만 관계가없는 매우 단순한 모델을 가정합니다.

이 프레젠테이션에서는 ObjectiveResourceASIHTTPRequest를 포함한 유용한 라이브러리에 대한 포인터와 함께 REST 기반 웹 애플리케이션과 동기화 된 코어 데이터 모델을 유지하는 방법에 대해서도 자세히 설명합니다 . 그것이 당신이하려는 일인지 확실하지 않지만 Core Data 코드도 살펴볼 가치가 있습니다.


7

당신이있는 경우 NSDate의견 중 하나 위에서 언급 한 바와 같이, 관리되는 개체에, 당신은을 포함하는 객체 직렬화 문제가있을 수 있습니다 NSDate. 간단한 수정은 목적 -c 범주 JSONDataRepresentationNSDate사용 하는 방법 을 추가하는 것 입니다.

다음 두 파일을 프로젝트에 추가하십시오.

NSdate.h :

#import <Foundation/Foundation.h>

@interface NSDate (jsondatarepresentation) 

- (NSData*) JSONDataRepresentation;

@end

NSDate.m :

#import "NSDate.h"

@implementation NSDate (jsondatarepresentation)

- (NSData*) JSONDataRepresentation {
    return [[[NSNumber numberWithDouble:[self timeIntervalSince1970]] stringValue] dataUsingEncoding:NSUTF8StringEncoding];
}

@end


2

나는 매우 잘 작동하는이 게시물을 보았습니다.

http://touchalicious.com/blog/2009/10/25/turn-core-data-models-into-json.html

이것은 재귀 적이므로 다 대다 관계는 계속 반복 될 것입니다. 이를 방지하기 위해 Core Data 모델에서 관계의 사용자 정보 사전에 "isExportable"키를 추가했습니다. 그런 다음이 키를 확인하고 키없이 관계를 반복하지 않도록 선택할 수 있습니다.

여기에 이미지 설명 입력

if ([property isKindOfClass:[NSRelationshipDescription class]])
    {
        NSRelationshipDescription *relationshipDescription = (NSRelationshipDescription *)property;

        if ([[[relationshipDescription userInfo] objectForKey:@"isExportable"] boolValue] == YES)
        {
            NSString *name = [relationshipDescription name];

            if ([relationshipDescription isToMany])
            {
                NSMutableArray *arr = [properties valueForKey:name];
                if (!arr)
                {
                    arr = [[NSMutableArray alloc] init];
                    [properties setValue:arr forKey:name];
                }

                for (NSManagedObject *o in [self mutableSetValueForKey:name])
                {
                    [arr addObject:[o propertiesDictionary]];
                }
            }
            else
            {
                NSManagedObject *o = [self valueForKey:name];
                [properties setValue:[o propertiesDictionary] forKey:name];
            }
        }
    }
}

2

이 질문에 대한 빠른 업데이트를 게시하는 것으로 생각했습니다. 나는 Marcus와 Brandon의 Answers를 따랐고 JSON 내보내기를 위해 이것을 생각해 냈습니다 (아직도 TouchJSON을 사용합니다).

- (NSData*)jsonStructureFromManagedObjects:(NSArray*)managedObjects
{
    NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
    NSData *jsonData      = [[CJSONSerializer serializer] serializeArray:objectsArray error:nil];
    return jsonData;
}

- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects
{
    NSMutableArray *dataArray = [[NSMutableArray alloc] init];
    for (NSManagedObject *managedObject in managedObjects) {
        [dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
    }
    return dataArray;
}

- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject*)managedObject
{
    NSDictionary *attributesByName        = [[managedObject entity] attributesByName];
    NSDictionary *relationshipsByName     = [[managedObject entity] relationshipsByName];
    NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
    [valuesDictionary setObject:[[managedObject entity] name] forKey:@"ManagedObjectName"];

    for (NSString *relationshipName in [relationshipsByName allKeys]) {

        NSRelationshipDescription *description = [[[managedObject entity] relationshipsByName] objectForKey:relationshipName];

        if ([[[description userInfo] objectForKey:@"isExportable"] boolValue] == YES) {

            if (![description isToMany]) {
                NSManagedObject *relationshipObject = [managedObject valueForKey:relationshipName];
                if (relationshipObject) {
                    [valuesDictionary setObject:[self dataStructureFromManagedObject:relationshipObject] forKey:relationshipName];
                }

                continue;
            }

            NSSet *relationshipObjects        = [managedObject valueForKey:relationshipName];
            NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];

            for (NSManagedObject *relationshipObject in relationshipObjects) {
                [relationshipArray addObject:[self dataStructureFromManagedObject:relationshipObject]];
            }

            [valuesDictionary setObject:relationshipArray forKey:relationshipName];

        }

    }
    return valuesDictionary;
}

가져 오기 작업을 수행 할 수 없었습니다. 아마도 Magical Record를 사용하고 있다는 사실과 관련이있을 수 있습니다. 확실하지 않으므로 들어오는 JSON 스트림을 반복하고 수동으로 개체를 생성합니다.


1

Marcus S. Zarra는 재귀 적 아이디어를 작업 버전으로 가져 오도록 영감을주었습니다. 이 버전에서는 CoreData에 키를 설정할 필요가 없으며 프로젝트에서 잘라내어 붙여 넣을 수 있습니다. :-)

// MARK: - encoding and decoding CoreData entity to dictionary

func dataStructureFromManagedObject( managedObject:NSManagedObject?, parentEntity: NSEntityDescription? = nil) -> NSMutableDictionary {
    if (managedObject != nil) {
        var attributesByName: NSDictionary = managedObject!.entity.attributesByName
        var relationshipsByName: NSDictionary  = managedObject!.entity.relationshipsByName
        var valuesImmutableDictionary: NSDictionary = managedObject!.dictionaryWithValuesForKeys( attributesByName.allKeys)
        var valuesDictionary: NSMutableDictionary = valuesImmutableDictionary.mutableCopy() as NSMutableDictionary
        valuesDictionary.setObject( managedObject!.entity.name!, forKey: "ManagedObjectName")
        for relationshipNameObject in relationshipsByName.allKeys {
            var relationshipName: NSString = relationshipNameObject as  NSString
            var relationshipDescription: NSRelationshipDescription? = relationshipsByName.objectForKey( relationshipName) as? NSRelationshipDescription
            if !relationshipDescription!.toMany {
                // ono to one
                if parentEntity == nil || (relationshipDescription! as NSRelationshipDescription).destinationEntity != parentEntity! {
                    // no parent or relationship is "downward" -> object for relationship must be added
                    var relationshipObject: NSManagedObject? = managedObject!.valueForKey( relationshipName) as? NSManagedObject
                    var relationshipObjectDictionary: NSMutableDictionary = self.dataStructureFromManagedObject( relationshipObject, parentEntity: managedObject?.entity)
                    valuesDictionary.setObject( relationshipObjectDictionary, forKey: relationshipName)
                } else {
                    // relationship is "upward" -> nothing to do
                }
            } else {
                // one to many -> all objects must be added
                var relationshipObjects: NSSet = managedObject!.mutableSetValueForKey( relationshipName)
                var relationshipArray:NSMutableArray = []
                for relationshipObjectRaw in relationshipObjects {
                    var relationshipObject:NSManagedObject? = relationshipObjectRaw as? NSManagedObject
                    if relationshipObject != nil && !relationshipObject!.entity.isKindOfEntity( managedObject!.entity) {
                        relationshipArray.addObject(self.dataStructureFromManagedObject( relationshipObject, parentEntity: managedObject?.entity))
                    }
                }
                valuesDictionary.setObject( relationshipArray, forKey: relationshipName)
            }
        }
        return valuesDictionary
    } else {
        return NSMutableDictionary()
    }
}

func managedObjectFromStructure( structureDictionary: NSDictionary, moc: NSManagedObjectContext, parentObject: NSManagedObject? = nil) -> NSManagedObject {
    if structureDictionary.count > 0 {
        var objectName:NSString = structureDictionary.objectForKey( "ManagedObjectName") as NSString
        var managedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName( objectName, inManagedObjectContext: moc) as NSManagedObject
        var relationshipsByName: NSDictionary  = managedObject.entity.relationshipsByName
        var realObjectStructure:NSMutableDictionary = structureDictionary.mutableCopy() as NSMutableDictionary
        realObjectStructure.removeObjectForKey( "ManagedObjectName")
        for key in realObjectStructure.allKeys {
            // search for "ManagedObjectName" relationship entrys and delete them before filling the managedObject from this structure
            for relationshipName in relationshipsByName.allKeys {
                if relationshipName as NSString == key as NSString {
                    realObjectStructure.removeObjectForKey( key)
                }
            }
        }
        managedObject.setValuesForKeysWithDictionary( realObjectStructure)
        // the main object with attributes is created. Now care about the relationships
        for relationshipName in managedObject.entity.relationshipsByName.keys {
            var description:NSRelationshipDescription = relationshipsByName.objectForKey( relationshipName) as NSRelationshipDescription
            if !description.toMany {
                // to one relationship
                if parentObject == nil || description.destinationEntity != parentObject!.entity {
                    // no parent or relationship is "downward" -> recurse structure to add
                    var childStructureDictionary:NSDictionary = structureDictionary.objectForKey( relationshipName) as NSDictionary
                    if childStructureDictionary.count > 0 {
                        // dictionary not empty -> object must be created and added
                        var childObject:NSManagedObject? = self.managedObjectFromStructure( childStructureDictionary, moc: moc, parentObject: managedObject)
                        // validateForUpdate
                        var error:NSError?
                        if !managedObject.validateForUpdate( &error) {
                            println("Error: Object not in valid state for update!!! -> \(error)")
                        } else {
                            managedObject.setValue( childObject, forKey: relationshipName as NSString)
                        }
                    } else {
                        // relationship is "upward" -> nothing to do
                    }
                }
            } else {
                // to many relationship
                var relationshipSet:NSMutableSet = managedObject.mutableSetValueForKey( relationshipName as NSString)
                var relationshipArray:NSArray = structureDictionary.objectForKey( relationshipName as NSString) as NSArray
                for childStructureDictionary in relationshipArray {
                    if childStructureDictionary.count > 0 {
                        // dictionary not empty -> object must be created and added
                        var childObject:NSManagedObject = self.managedObjectFromStructure( childStructureDictionary as NSDictionary, moc: moc, parentObject: managedObject)
                        // validateForUpdate
                        var error:NSError?
                        if !managedObject.validateForUpdate( &error) {
                            println( "Error: Object not in valid state for update!!! -> \(error)")
                        } else {
                            relationshipSet.addObject( childObject)
                        }
                    } else {
                        // no object was behind the relationship -> nothing to do
                    }
                }
                // save set
                managedObject.setValue( relationshipSet, forKey: relationshipName as NSString)
            }
        }
        // final check validateForUpdate
        var error:NSError?
        if !managedObject.validateForUpdate( &error) {
            println( "Error: Object not in valid state for update although all previous check are passed!!! -> \(error)")
        }
        return managedObject
    } else {
        println( "Error: structure for object was empty. this should not happen at this point")
        var objectName:NSString = structureDictionary.objectForKey( "ManagedObjectName") as NSString
        var managedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName( objectName, inManagedObjectContext: moc) as NSManagedObject
        return managedObject
    }
}

func dataStructuresFromManagedObjects( managedObjects: NSArray) -> NSArray {
    var dataArray:NSMutableArray = []
    for managedObject in managedObjects {
        dataArray.addObject( self.dataStructureFromManagedObject(managedObject as? NSManagedObject))
    }
    return dataArray
}

여기서 핵심은 부모 엔티티를 재귀에 대한 인수로 전달하여 데이터로 채워야하는 관계를 결정할 수 있도록하는 것입니다. 양쪽 기능 그래서 : dataStructureFromManagedObjectmanagedObjectFromStructure인코딩 대상으로 사전과 뒷면에 CoreData에서 어떤 엔티티 객체를 디코딩 할 수있다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.