4

更新:解决方案记录在下面的答案中

我在使用 0.20.3 中的新 @root @parent 访问器进行 RestKit 映射时遇到问题。我不确定这是错误还是对如何正确使用框架的误解。

问题

@root 和 @parent 的新概念似乎对我不起作用。

编辑:删除了一堆关于我认为问题所在的讨论。我错了,所以没有必要消化它。如果上面的问题陈述适用于您……那么这篇 SO 帖子可能会帮助您继续前进。

背景

示例源 XML 可以在这里下载

XML的基本结构如下:

<locations>
  <location lat="38.8561" lon="-94.6654" timezone="UTC" city="Overland Park" region="KS" country="US" zipcode="66223" offset="0" local_offset_hours="-5">
    <sfc_ob>
      <attribute1></attribute1>
    </sfc_ob>
    <daily_summaries>
      <daily_summary>
        <attribute2> </attribute2>
      </daily_summary>
    </daily_summaries>
    <hourly_summaries>
      <hourly_summary>
        <attribute3></attribute3>
      </hourly_summary>
    </hourly_summaries>
  </location>
</locations>

我的核心数据实体如下:

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

RESTKIT 相关代码

- (GLWeatherManager *)init {
self = [super init];

// setup logging
RKLogConfigureByName("RestKit/Network*", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);

self.httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://weather.wdtinc.com"]];
[self.httpClient setDefaultHeader:@"Accept" value:RKMIMETypeXML];
[RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:@"application/xml"];
self.restKitManager = [[RKObjectManager alloc] initWithHTTPClient:self.httpClient];
self.restKitManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:[NSPersistentStoreCoordinator MR_defaultStoreCoordinator]];
[self.restKitManager.managedObjectStore createManagedObjectContexts];

// Locations
RKEntityMapping *locationMapping = [self buildMapForLocations];
RKEntityMapping *currentConditionsMapping = [self buildMapForCurrentConditions];
RKEntityMapping *dailySummariesMapping = [self buildMapForDailySummaries];
RKEntityMapping *hourlyForecastsMapping = [self buildMapForHourlyForecasts];

[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"daily_summaries" toKeyPath:@"dailySummaries" withMapping:dailySummariesMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"hourly_summaries" toKeyPath:@"hourlyForecasts" withMapping:hourlyForecastsMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"sfc_ob" toKeyPath:@"currentConditions" withMapping:currentConditionsMapping]];


[dailySummariesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"location" withMapping:locationMapping]];
[hourlyForecastsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil toKeyPath:@"location" withMapping:locationMapping]];

RKResponseDescriptor *descriptor = [RKResponseDescriptor  responseDescriptorWithMapping:locationMapping pathPattern:@"/feeds/demofeeds20131031/mega.php" keyPath:@"locations.location" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];                        

// add mapping description to objectmanager
[self.restKitManager addResponseDescriptor:descriptor];

RKResponseDescriptor *descriptor2 = [RKResponseDescriptor responseDescriptorWithMapping:currentConditionsMapping pathPattern:@"/feeds/demofeeds20131031/mega.php" keyPath:@"locations.location.sfc_ob"                                                              statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

[self.restKitManager addResponseDescriptor:descriptor2];

RKResponseDescriptor *descriptor3 = [RKResponseDescriptor responseDescriptorWithMapping:dailySummariesMapping                                    pathPattern:@"/feeds/demofeeds20131031/mega.php"                                                 keyPath:@"locations.location.daily_summaries.daily_summary"                                          statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [self.restKitManager addResponseDescriptor:descriptor3];

    RKResponseDescriptor *descriptor4 = [RKResponseDescriptor responseDescriptorWithMapping:hourlyForecastsMapping                                               pathPattern:@"/feeds/demofeeds20131031/mega.php"                                                                keyPath:@"locations.location.hourly_summaries.hourly_summary"
                                                                                statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [self.restKitManager addResponseDescriptor:descriptor4];

// start the location manager to get the current location
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager setDelegate:self];
[self.locationManager startUpdatingLocation];

self.locations = [NSMutableArray arrayWithArray:[Locations findAll]];

[self getMegaFeed];

return self;

}


- (RKEntityMapping *)buildMapForLocations {
RKEntityMapping *locationMapping = [RKEntityMapping mappingForEntityForName:@"Locations" inManagedObjectStore:self.restKitManager.managedObjectStore];
[locationMapping addAttributeMappingsFromDictionary:@{
 @"lat" : @"latitude",
 @"lon" : @"longitude",
 @"city" : @"city",
 @"region" : @"region",
 @"country" : @"country",
 @"zipcode" : @"zipcode",
 }];
locationMapping.identificationAttributes = [NSArray arrayWithObject:@"zipcode"];

return locationMapping;
}

    - (RKEntityMapping *)buildMapForCurrentConditions {
    // Current Conditions
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"CurrentConditions" inManagedObjectStore:self.restKitManager.managedObjectStore];
    [mapping addAttributeMappingsFromDictionary:@{
     //@"stn" : @"stn",
     //@"location" : @"location",
     //@"stn_lat" : @"stnLatitude",
     //@"stn_lon" : @"stnLongitude",
     @"ob_time.text" : @"observationTime",
     @"day_of_week.text" : @"dayOfWeek",
     @"temp_C.text" : @"temperatureMetric",
     @"temp_F.text" : @"temperatureImperial",
     @"dewp_C.text" : @"dewPointMetric",
     @"dewp_F.text" : @"dewPointImperial",
     @"rh_pct.text" : @"relativeHumidity",
     @"wnd_dir.text" : @"windDirection",
     @"wnd_spd_mph.text" : @"windSpeedImperial",
     @"wnd_spd_kph.text" : @"windSpeedMetric",
     @"press_in.text" : @"pressureImperial",
     @"press_mb.text" : @"pressureMetric",
     @"wx.text" : @"conditionSummary",
     @"wx_code.text" : @"conditionCode",
     @"cld_cover.text" : @"cloudCover",
     @"visibility_ft.text" : @"visibilityImperial",
     @"visibility_m.text" : @"visibilityMetric",
     @"apparent_temp_F.text" : @"feelsLikeTemperatureImperial",
     @"apparent_temp_C.text" : @"feelsLikeTemperatureMetric",
     @"moon_phase.text" : @"moonPhase",
     @"sunrise_utc.text" : @"sunrise",
     @"sunset_utc.text" : @"sunset"
     }];

    [mapping setIdentificationAttributes:[NSArray arrayWithObjects:@"observationTime", nil]];

    return mapping;

}

- (RKEntityMapping *)buildMapForDailySummaries {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"DailySummaries" inManagedObjectStore:self.restKitManager.managedObjectStore];
[mapping addAttributeMappingsFromDictionary:@{
 @"summary_date.text" : @"date",
 @"day_of_week.text" : @"dayOfWeek",
 @"max_temp_F.text" : @"tempMaxImperial",
 @"max_temp_C.text" : @"tempMaxMetric",
 @"min_temp_F.text" : @"tempMinImperial",
 @"min_temp_C.text" : @"tempMinMetric",
 @"wnd_spd_mph.text" : @"windSpeedImperial",
 @"wnd_spd_kph.text" : @"windSpeedMetric",
 @"min_wnd_spd_mph.text" : @"windSpeedMinImperial",
 @"min_wnd_spd_kph.text" : @"windSpeedMinMetric",
 @"max_wnd_spd_mph.text" : @"windSpeedMaxImperial",
 @"max_wnd_spd_kph.text" : @"windSpeedMaxMetric",
 @"wnd_gust_mph.text" : @"windGustImperial",
 @"wnd_gust_kph.text" : @"windGustMetric",
 @"wnd_dir.text" : @"windDirection",
 @"pop.text" : @"probabilityOfPrecipitation",
 @"wx.text" : @"conditionSummary",
 @"wx_code.text" : @"conditionCode",
 @"text_description.text" : @"textDescription",
 @"sunrise_utc.text" : @"sunrise",
 @"sunset_utc.text" : @"sunset",
 @"@root.locations.location.zipcode" : @"locationZipcode"
 }];
mapping.identificationAttributes = [NSArray arrayWithObjects:@"date", @"locationZipcode", nil];
[mapping addConnectionForRelationship:@"location" connectedBy:@{@"locationZipcode": @"zipcode"}];
return mapping;

}

- (RKEntityMapping *)buildMapForHourlyForecasts {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"HourlyForecasts" inManagedObjectStore:self.restKitManager.managedObjectStore];
[mapping addAttributeMappingsFromDictionary:@{
 @"day_of_week_utc.text" : @"dayOfWeek",
 @"time_utc.text" : @"forecastTime",
 @"temp_C.text" : @"temperatureMetric",
 @"temp_F.text" : @"temperatureImperial",
 @"dewp_C.text" : @"dewPointMetric",
 @"dewp_F.text" : @"dewPointImperial",
 @"app_temp_C.text" : @"feelsLikeTemperatureMetric",
 @"app_temp_F.text" : @"feelsLikeTemperatureImperial",
 @"rh_pct.text" : @"relativeHumidity",
 @"wx.text" : @"conditionSummary",
 @"wx_code.text" : @"conditionCode",
 @"day_night.text" : @"dayNight",
 @"pop.text" : @"probabilityOfPrecipitation",
 @"sky_cov_pct.text" : @"skyCoverPercent",
 @"wnd_dir.text" : @"windDirection",
 @"wnd_dir_degs.text" : @"windDirectionDegrees",
 @"wnd_spd_mph.text" : @"windSpeedImperial",
 @"wnd_spd_kph.text" : @"windSpeedMetric",
 @"@root.locations.location.zipcode" : @"locationZipcode"
 }];

mapping.identificationAttributes = [NSArray arrayWithObjects:@"forecastTime", @"locationZipcode", nil];
[mapping addConnectionForRelationship:@"location" connectedBy:@{@"locationZipcode": @"zipcode"}];
return mapping;

}

- (void)getMegaFeed {
for (Locations *location in self.locations) {
    NSString *path = [NSString stringWithFormat:@"/feeds/demofeeds20131031/mega.php?ZIP=%@&UNITS=all",location.zipcode];
    // fetch data
    [self.restKitManager getObjectsAtPath:path
                               parameters:nil
                                  success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {

                                      NSArray *mappedObjects = [mappingResult array];
                                      NSMutableArray *validObjectIDs = [[NSMutableArray alloc] initWithCapacity:[mappedObjects count]];

                                      for (NSManagedObject *object in mappedObjects) {
                                          NSManagedObjectID *moID = [object objectID];
                                          [validObjectIDs addObject:moID];
                                      }
                                      [self notifyObservers:@selector(megaFeedDidFinish:location:) withObject:validObjectIDs withObject:location];


                                  }
                                  failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                      [REUtility showDefaultAlertWithError:error];
                                      [RELog error:@"%s Hit error:%@", __func__, error];
                                  }];

}

}

4

2 回答 2

5

我认为问题在于您在尝试使用@root 的映射的响应描述符中使用的关键路径。当您在响应描述符上指定键路径时,您实际上是在更改 @root 对象,因为您正在将内容深入到指定的深度,并且该深度成为该映射的根。如果您在映射过程中进行调试并查看提供的元数据,您应该能够看到/验证这一点。

我不清楚为什么你有这么多不同的响应描述符。对于其映射定义所有不同部分(及其映射)之间的所有关系的位置具有一个响应描述符似乎更合乎逻辑。以这种方式工作,您将拥有更简单的代码,并且您还可以访问@root / @parent。

于 2013-10-06T09:02:58.983 回答
1

堆垛机同胞

这是由于上述韦恩的建议而实现的解决方案。我在这里发帖不是为了收集积分,而是为了将问题和最终解决方案的冗长内容分开。请将任何赞成票授予@Wain,我只是想发布一个关于此问题的简明问题到解决方案示例。

我发现 RestKit 是我见过的最棒的(出于多种原因)第 3 方框架之一。有很多社区参与;但是,对于“超出范围”或不常见的场景和用法,文档和示例很少。我怀疑大多数人使用带有 JSON 的 RestKit,而现实世界中复杂的 XML 示例很少。我希望这可以帮助其他有相同类型问题的人。

解决方案

Wain 是正确的,因为我正在构建多个响应描述符,目标是我所追求的 XML 层次结构中的每个点(在上面的问题中可用)。这阻止了 RestKit 理解曾经有一个(根)或(父)可以访问。

因为我需要将这些映射到 Core Data 中的多个实体,所以我应该在 XML 的最高标记级别拥有一个大型响应描述符。

这意味着如果我映射单个(在我的 xml 中)高级位置标记并且我已经正确配置了 RestKit 关系,它将为您完成其余的工作。

执行

我意识到的第一件事是我不关心 XML 中的高级 Locations 标记,也不需要将其映射到核心数据实体,因此我构建了 RKObjectMapping 而不是 RKEntityMapping 来处理XML 中的高级标记。

完整的解决方案如下,但我将逐步完成更改。

注意新RKObjectMapping *locationsMapping = [self buildMapForLocations];的和相应的方法。

然后我需要告诉新的位置映射关于要映射到核心数据的位置实体

[locationsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location"  withMapping:locationMapping]];

多亏了 Wain,下一个重大变化是关系的关键路径更改,如下所示:

[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"daily_summaries.daily_summary" toKeyPath:@"dailySummaries" withMapping:dailySummariesMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"hourly_summaries.hourly_summary" toKeyPath:@"hourlyForecasts" withMapping:hourlyForecastsMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"sfc_ob" toKeyPath:@"currentConditions" withMapping:currentConditionsMapping]];

在这里,我们告诉 locationMapping(映射到 Core Data)我们从源 XML 映射到我们在 Core Data 中的位置。由于我们在位置映射上,因此在源 XML 中,我的 dailySummaries(实体)的 keyPath 是“daily_summaries.daily_summary”。同样,hourlyForecasts 的正确 keyPath 是“hourly_summaries.hourly_summary”,并且从 XML“sfc_ob”中的位置映射到 currentConditions(实体)。

最后,除了一个简单的响应描述符之外,我所有的响应描述符都消失了。

RKResponseDescriptor *descriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationsMapping
                                                                                method:RKRequestMethodGET
                                                                           pathPattern:@"/feeds/demofeeds20131031/mega.php"
                                                                               keyPath:@"locations"
                                                                           statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.restKitManager addResponseDescriptor:descriptor];

问题陈述(上图)中调用 RestKit 的成功块中的所有内容都被删除,因为包括关系在内的所有内容都已正确映射。请注意,映射的所有问题陈述代码仍然有效,除了对解决方案的@root @parent 方面进行了细微调整。

在映射每日摘要和每小时预测时,我仍然需要从该位置获取邮政编码以填充到核心数据实体中,以便 RestKit 可以在未来调用时将其用作键来检查数据库以查看是否具有此键的记录已经存在(更新而不是创建)。

我调整了@映射以从位置获取邮政编码。请注意对层次结构的@parent.@parent 遍历的预期用途,如下所示:

- (RKEntityMapping *)buildMapForDailySummaries {
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"DailySummaries" inManagedObjectStore:self.restKitManager.managedObjectStore];
    [mapping addAttributeMappingsFromDictionary:@{
     @"summary_date.text" : @"date",
     @"day_of_week.text" : @"dayOfWeek",
     @"max_temp_F.text" : @"tempMaxImperial",
     @"max_temp_C.text" : @"tempMaxMetric",
     @"min_temp_F.text" : @"tempMinImperial",
     @"min_temp_C.text" : @"tempMinMetric",
     @"wnd_spd_mph.text" : @"windSpeedImperial",
     @"wnd_spd_kph.text" : @"windSpeedMetric",
     @"min_wnd_spd_mph.text" : @"windSpeedMinImperial",
     @"min_wnd_spd_kph.text" : @"windSpeedMinMetric",
     @"max_wnd_spd_mph.text" : @"windSpeedMaxImperial",
     @"max_wnd_spd_kph.text" : @"windSpeedMaxMetric",
     @"wnd_gust_mph.text" : @"windGustImperial",
     @"wnd_gust_kph.text" : @"windGustMetric",
     @"wnd_dir.text" : @"windDirection",
     @"pop.text" : @"probabilityOfPrecipitation",
     @"wx.text" : @"conditionSummary",
     @"wx_code.text" : @"conditionCode",
     @"text_description.text" : @"textDescription",
     @"sunrise_utc.text" : @"sunrise",
     @"sunset_utc.text" : @"sunset",
     @"@parent.@parent.zipcode" : @"locationZipcode"
     }];
    mapping.identificationAttributes = [NSArray arrayWithObjects:@"date", @"locationZipcode", nil];
    [mapping addConnectionForRelationship:@"location" connectedBy:@{@"locationZipcode": @"zipcode"}];


    return mapping;
}

- (RKEntityMapping *)buildMapForHourlyForecasts {
    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"HourlyForecasts" inManagedObjectStore:self.restKitManager.managedObjectStore];
    [mapping addAttributeMappingsFromDictionary:@{
     @"day_of_week_utc.text" : @"dayOfWeek",
     @"time_utc.text" : @"forecastTime",
     @"temp_C.text" : @"temperatureMetric",
     @"temp_F.text" : @"temperatureImperial",
     @"dewp_C.text" : @"dewPointMetric",
     @"dewp_F.text" : @"dewPointImperial",
     @"app_temp_C.text" : @"feelsLikeTemperatureMetric",
     @"app_temp_F.text" : @"feelsLikeTemperatureImperial",
     @"rh_pct.text" : @"relativeHumidity",
     @"wx.text" : @"conditionSummary",
     @"wx_code.text" : @"conditionCode",
     @"day_night.text" : @"dayNight",
     @"pop.text" : @"probabilityOfPrecipitation",
     @"sky_cov_pct.text" : @"skyCoverPercent",
     @"wnd_dir.text" : @"windDirection",
     @"wnd_dir_degs.text" : @"windDirectionDegrees",
     @"wnd_spd_mph.text" : @"windSpeedImperial",
     @"wnd_spd_kph.text" : @"windSpeedMetric",
     @"@parent.@parent.zipcode" : @"locationZipcode"
     }];

    mapping.identificationAttributes = [NSArray arrayWithObjects:@"forecastTime", @"locationZipcode", nil];
    [mapping addConnectionForRelationship:@"location" connectedBy:@{@"locationZipcode": @"zipcode"}];
    return mapping;
}
- (RKObjectMapping *)buildMapForLocations {
    RKObjectMapping *locationsMapping = [[RKObjectMapping alloc] initWithClass:[WDTMegaFeedResponse class]];
    [locationsMapping addAttributeMappingsFromDictionary:@{
     @"language.text" : @"language",
                                                           }];
    return locationsMapping;
}

此处对映射的更改以及以下更改是此用例的完整解决方案。

- (GLWeatherManager *)init {
    self = [super init];

    self.cloud = [[Kumulos alloc] init];
    [self.cloud setDelegate:self];

    // Check to see if the states have been loaded
    NSArray *states = [States findAllSortedBy:@"code" ascending:YES];
    if ([states count] == 0) {
        [self.cloud getStates];
    }

    // setup logging
    RKLogConfigureByName("RestKit/Network*", RKLogLevelTrace);
    RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);

    // initialize the network layer and ensure we are using the same store magical record is using
    self.httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://weather.wdtinc.com"]];
    [self.httpClient setDefaultHeader:@"Accept" value:RKMIMETypeXML];
    [RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:@"application/xml"];

    self.restKitManager = [[RKObjectManager alloc] initWithHTTPClient:self.httpClient];
    self.restKitManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:[NSPersistentStoreCoordinator MR_defaultStoreCoordinator]];
    [self.restKitManager.managedObjectStore createManagedObjectContexts];

    // Locations

    RKObjectMapping *locationsMapping = [self buildMapForLocations];

    RKEntityMapping *hourlyForecastsMapping = [self buildMapForHourlyForecasts];
    RKEntityMapping *dailySummariesMapping = [self buildMapForDailySummaries];
    RKEntityMapping *currentConditionsMapping = [self buildMapForCurrentConditions];
    RKEntityMapping *locationMapping = [self buildMapForLocation];

    [locationsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location"  withMapping:locationMapping]];

    [locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"daily_summaries.daily_summary" toKeyPath:@"dailySummaries" withMapping:dailySummariesMapping]];
    [locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"hourly_summaries.hourly_summary" toKeyPath:@"hourlyForecasts" withMapping:hourlyForecastsMapping]];
    [locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"sfc_ob" toKeyPath:@"currentConditions" withMapping:currentConditionsMapping]];

    RKResponseDescriptor *descriptor = [RKResponseDescriptor responseDescriptorWithMapping:locationsMapping
                                                                                    method:RKRequestMethodGET
                                                                               pathPattern:@"/feeds/demofeeds20131031/mega.php"
                                                                                   keyPath:@"locations"
                                                                               statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
    [self.restKitManager addResponseDescriptor:descriptor];
}

还有一些人可能会发现用于捕获高级 XML 标记的 RKObjectMapping 对象的头文件很有用。

#import <Foundation/Foundation.h>
#import "Locations.h"

@interface WDTMegaFeedResponse : NSObject

@property (nonatomic, strong) NSString *language;
@property (nonatomic, strong) Locations *location;

@end
于 2013-10-09T00:28:00.617 回答