我在使用 RestKit 映射关系 (0.20.3) 时遇到问题。我看过其他帖子,但还没有找到解决方案。我想我已经很接近了,但仍然缺少一些东西。
问题
实体属性可以很好地映射到核心数据;但是,如果实体具有关系,则它不会被映射。我有一个解决方法,我需要摆脱它,因为它现在已达到极限。
对于解决方法,在成功映射后,我已将托管对象 ID 传递给前台线程以水合有效的托管对象。然后我遍历对象并建立关系,然后“重新保存”到 Core Data。
解决方法的问题是映射关系是 IdentificationAttributes 参数的唯一键的一部分。由于映射在后台无法正常工作,因此无法正确识别现有记录,因此在未来的 API 调用中会出现重复条目。
背景
示例源 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:@"locations.location.daily_summaries" toKeyPath:@"dailySummaries" withMapping:dailySummariesMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location.hourly_summaries" toKeyPath:@"hourlyForecasts" withMapping:hourlyForecastsMapping]];
[locationMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location.sfc_ob" toKeyPath:@"currentConditions" withMapping:currentConditionsMapping]];
[currentConditionsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location" toKeyPath:@"location" withMapping:locationMapping]];
[dailySummariesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location" toKeyPath:@"location" withMapping:locationMapping]];
[hourlyForecastsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"locations.location" 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",
@"locations.location.zipcode.text" : @"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",
@"locations.location.zipcode.text" : @"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];
}];
}
}
更新
@WAIN
我尝试按照您的建议更改代码。当前条件对象似乎可以正确接收位置,但它是一对一的关系。其他是一对多的,仍然没有正确映射。测试的代码更改如下:
[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]];
[currentConditionsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];
[dailySummariesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];
[hourlyForecastsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];