如何使用 RestKit 将嵌套的 JSON 对象存储到核心数据中

Posted

技术标签:

【中文标题】如何使用 RestKit 将嵌套的 JSON 对象存储到核心数据中【英文标题】:How to store nested JSON object into Core Data using RestKit 【发布时间】:2014-04-16 14:22:34 【问题描述】:

我正在尝试实现一个我认为非常简单的用例,但我不确定如何正确执行它。

当在我的应用程序中加载特定屏幕时,我需要调用返回嵌套 JSON 对象的 Web 服务,然后解析所有数据并保存到一系列 Core Data 托管对象中。每当用户离开并返回该屏幕时,我想用从服务器返回的新数据替换当前在我的 Core Data 存储中的所有数据。

这是高级概述。看起来很简单。

setupRestKit 在应用加载时被调用:

+ (void)setupRestKit

    RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BASE_URL]];
    [RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:@"application/vnd.collection+json"];

    NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"Mobile" ofType:@"momd"]];
    NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];

    [managedObjectStore createPersistentStoreCoordinator];

    NSString *documentPath = [NIAppDelegate applicationDocumentsDirectory].relativePath;
    NSError *error;
    [managedObjectStore addSQLitePersistentStoreAtPath:[NSString stringWithFormat:@"%@/Mobile.sqlite", documentPath]
                                fromSeedDatabaseAtPath:nil
                                     withConfiguration:nil
                                               options:nil
                                                 error:&error];

    [managedObjectStore createManagedObjectContexts];

    manager.managedObjectStore = managedObjectStore;

    NSArray *rkClasses = [self allNIRestKitClasses];
    NSArray *descriptors = [self allDescriptorsFromRKClasses:rkClasses];

    [manager addResponseDescriptorsFromArray:descriptors];

第 25 行的 NSArray *descriptors 填充了一个描述符数组,其中包括以下返回的描述符对象:

+ (RKResponseDescriptor *)getDescriptor

    RKManagedObjectStore *store = [RKObjectManager sharedManager].managedObjectStore;

    RKEntityMapping *scheduleMapping = [RKEntityMapping mappingForEntityForName:@"Schedule" inManagedObjectStore:store];
    [scheduleMapping addAttributeMappingsFromDictionary:@@"version": @"version",
                                                      @"href": @"href"];

    RKEntityMapping *scheduleItemMapping = [RKEntityMapping mappingForEntityForName:@"ScheduleItem" inManagedObjectStore:store];
    [scheduleItemMapping addAttributeMappingsFromDictionary:@@"href": @"href"];

    RKEntityMapping *scheduleDataMapping = [RKEntityMapping mappingForEntityForName:@"ScheduleData" inManagedObjectStore:store];
    [scheduleDataMapping addAttributeMappingsFromDictionary:@@"AllCurricConfirmed": @"allCurricConfirmed",
                                                              @"EndDateTime": @"endDateTime",
                                                              @"EventLocation": @"eventLocation",
                                                              @"Notes": @"notes",
                                                              @"RecordID": @"recordID",
                                                              @"ScheduleType": @"scheduleType",
                                                              @"StartDateTime": @"startDateTime",
                                                              @"Title": @"title"];

    RKEntityMapping *scheduleLinkMapping = [RKEntityMapping mappingForEntityForName:@"ScheduleLink" inManagedObjectStore:store];
    [scheduleLinkMapping addAttributeMappingsFromDictionary:@@"rel": @"rel",
                                                              @"href": @"href"];

    RKRelationshipMapping *scheduleLinksInScheduleItem = [RKRelationshipMapping relationshipMappingFromKeyPath:@"link"
                                                                                                     toKeyPath:@"scheduleLinks"
                                                                                                   withMapping:scheduleLinkMapping];
    [scheduleItemMapping addPropertyMapping:scheduleLinksInScheduleItem];

    RKRelationshipMapping *scheduleDatasInScheduleItem = [RKRelationshipMapping relationshipMappingFromKeyPath:@"data"
                                                                                                 toKeyPath:@"scheduleDatas"
                                                                                               withMapping:scheduleDataMapping];
    [scheduleItemMapping addPropertyMapping:scheduleDatasInScheduleItem];

    RKRelationshipMapping *scheduleItemsInSchedule = [RKRelationshipMapping relationshipMappingFromKeyPath:@"items"
                                                                                                 toKeyPath:@"scheduleItems"
                                                                                               withMapping:scheduleItemMapping];
    [scheduleMapping addPropertyMapping:scheduleItemsInSchedule];

    NSIndexSet *scheduleStatusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
    RKResponseDescriptor *scheduleDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:scheduleMapping
                                                                                         method:RKRequestMethodGET
                                                                                    pathPattern:nil
                                                                                        keyPath:@"collection"
                                                                                    statusCodes:scheduleStatusCodes];

    return scheduleDescriptor;

那些描述符和关系完全包含我的嵌套 JSON 对象,我相信它们可以正常工作。

我想我的问题是 RestKit 和 Core Data 如何协同工作。我对这两种技术都比较陌生。以下 sn-p 描述了我的 ViewController 为从 web 服务获取数据所做的工作:

// ViewController.m
- (void)getData

    [SVProgressHUD showWithStatus:@"Downloading Schedule"];

    [NIRestKitSchedule getScheduleWithBlock:^(BOOL valid) 
        if (valid) 
            // get the data from Core Data (because I assume that RestKit has saved everything into it already?)
            NSManagedObjectContext *mainContext = RKObjectManager.sharedManager.managedObjectStore.mainQueueManagedObjectContext;
            NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Schedule"];
            NSArray *schedule = [mainContext executeFetchRequest:fetch error:nil];

            // TODO: and do stuff with it

            [SVProgressHUD dismiss];
         else 
            [SVProgressHUD showErrorWithStatus:@"Could not get Schedule"];
        
    ];


// NIRestKitSchedule.m
+ (void)getScheduleWithBlock:(void (^)(BOOL))valid

    [self deleteEntity:@"Schedule"];

    NSURL *scheduleUrl = [RKObjectManager sharedManager].baseURL;

    scheduleUrl = [scheduleUrl URLByAppendingPathComponent:SCHEDULE_ENDPOINT];

    scheduleUrl = [scheduleUrl URLByAppendingDefaultParameters];
    [[RKObjectManager sharedManager] getObjectsAtPath:[scheduleUrl absoluteString]
                                           parameters:nil
                                              success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) 
                                                  valid(YES);
                                              
                                              failure:^(RKObjectRequestOperation *operation, NSError *error) 
                                                  valid(NO);
                                              ];



+ (void)deleteEntity:(NSString *)entityName

    NSManagedObjectContext *mainContext = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext;

    NSFetchRequest *fetchObjects = [[NSFetchRequest alloc] init];
    [fetchObjects setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:mainContext]];
    [fetchObjects setIncludesPropertyValues:NO];

    NSError *error = nil;
    NSArray *objects = [mainContext executeFetchRequest:fetchObjects error:&error];

    for (NSManagedObject *object in objects) 
        [mainContext deleteObject:object];
    
    NSError *saveError = nil;
    [mainContext save:&saveError];

在编译应用程序并首次运行时,最后一个代码 sn-p 中的schedule 正确加载了嵌套的数据数组。但随后对getData 的任何调用都会导致Core Data “故障”。

我应该打电话给getObjectsAtPath:parameters:success:failure吗?我应该在哪些上下文中存储和删除我的日程安排数据?有人可以指出我正确的方向吗? RestKit 现在有点让我不知所措。

【问题讨论】:

当您说“故障”时,您的意思是日志语句中打印的内容? @Wain 正确:(lldb) po schedule <_PFArray 0x14dd6c80>( <Schedule: 0x14d6f190> (entity: Schedule; id: 0x14d32c60 <x-coredata://D88456A9-CBDD-4E92-B746-989C5E00B459/Schedule/p3> ; data: <fault>) ) 认为我缺乏理解与托管对象上下文有关。 那里的错误只是意味着数据还没有加载到内存中-不用担心 【参考方案1】:

您的代码通常应该使用mainQueueManagedObjectContext,这样看起来不错。

当你想保存时,你应该在上下文中调用saveToPersistentStore: 而不是save:

是的,您应该调用 getObjectsAtPath:parameters:success:failure,因为这是从服务器获取数据的原因。

您不需要自己删除对象,请查看fetch request blocks。

【讨论】:

好的!在我的deleteEntity: 方法中调用saveToPersistentStore: 而不是save: 可以清除我的Core Data 故障,非常感谢!现在我将研究获取请求块以及它们与删除托管对象的关系。 可能会有所帮助:***.com/questions/21232402/…

以上是关于如何使用 RestKit 将嵌套的 JSON 对象存储到核心数据中的主要内容,如果未能解决你的问题,请参考以下文章

RestKit RKObjectMapping .. 嵌套和一次性对象

在其 HTTP 正文中嵌套 JSON 的 RestKit 对象映射请求

RestKit:将嵌套数组映射到对象

Restkit 0.20 嵌套对象数组映射问题

如何递归加载RestKit中由外键标识的嵌套对象?

Restkit嵌套对象映射问题