Restkit nested object mapping weird issue

时间:2016-06-18 20:00:54

标签: dynamic nested mapping restkit-0.26.x

I'm trying to map the following JSON response with Restkit:

{
  "container": {
    "id": 1,
    "infos": [
      {
        "id": 1,
        "name": "...",
        "type": 6,
        "value": {
          "d0": {
            "periods": [ 
              { "close": "12.30", "open": "09.00" },
              { "close": "19.30", "open": "15.30" }
             ]
          },
          "d1": {...},
          "d2": {...},
          "d3": {...},
          "d4": {...},
          "d5": {...},
          "d6": {...}
        },
        "created": "2015-12-08T17:18:05.732Z",
      }
    ]
  }
}

where the structure inside "value" is dynamic - that is can vary according to the type of response returned by the API. The object classes implemented are as follows:

@interface CFFContainer : NSObject
@property (nonatomic, retain) NSNumber *identifier;
@property (nonatomic, retain) NSMutableArray *infos;
@end

@interface CFFInfo : NSObject
@property (nonatomic, retain) NSNumber *identifier;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSNumber *type;
@property (nonatomic, retain) NSMutableDictionary *value;
@property (nonatomic, retain) NSDate *created;
@end

@interface CFFOpeningTimes : NSObject
@property (nonatomic, retain) CFFOpeningDay *d0;
@property (nonatomic, retain) CFFOpeningDay *d1;
@property (nonatomic, retain) CFFOpeningDay *d2;
@property (nonatomic, retain) CFFOpeningDay *d3;
@property (nonatomic, retain) CFFOpeningDay *d4;
@property (nonatomic, retain) CFFOpeningDay *d5;
@property (nonatomic, retain) CFFOpeningDay *d6;
@end

@interface CFFOpeningDay : NSObject 
@property (nonatomic, retain) NSMutableArray *periods;
@end

@interface CFFPeriod : NSObject
@property(nonatomic, retain) NSString *open;
@property(nonatomic, retain) NSString *close;
@end

(not sure if I can use a generic NSMutableDictionary for CFFInfo.value, but I cannot assign a specific type, since I do not know what the actual structure of the entity will be). And finally the mappings:

// Container.
[containerMapping addAttributeMappingsFromDictionary:@{@"identifier": @"id"}];
RKRelationshipMapping *infoMapping = 
[RKRelationshipMapping relationshipMappingFromKeyPath:@"infos"
                                            toKeyPath:@"infos"
                                          withMapping:[CFFInfo mapping]];
[responseMapping addPropertyMapping:infoMapping];

// Info.
[infoMapping addAttributeMappingsFromDictionary:@{@"identifier": @"id", @"name": @"name", @"type": @"type", @"created": @"created"}];

// Create dynamic mapping for surveys, opening times, news, etc.    
RKDynamicMapping *dynamicMapping = [[RKDynamicMapping alloc] init];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
    if ([[representation valueForKey:@"name"] containsString:@"Survey"]) {
        return [CFFSurvey mapping];
    } else if ([representation valueForKey:@"d0"] != nil) {
        return [CFFOpeningTimes mapping];
    } else if ([[representation valueForKey:@"name"] containsString:@"News"]) {
        return [CFFNews mapping];
    }
    return nil;
}];

RKRelationshipMapping *valueMapping =
[RKRelationshipMapping relationshipMappingFromKeyPath:@"value"
                                            toKeyPath:@"value"
                                          withMapping:dynamicMapping];
[infoMapping addPropertyMapping: valueMapping];

// Opening times.
RKRelationshipMapping * d0mapping =
[RKRelationshipMapping relationshipMappingFromKeyPath:@"d0"
                                            toKeyPath:@"d0"
                                          withMapping:[CFFOpeningDay mapping]];
[openingTimesMapping addPropertyMapping:d0mapping];
// ... same for d1, d2, d3, d4, d5, d6...

// Opening day.
RKRelationshipMapping *periodsMapping =
[RKRelationshipMapping relationshipMappingFromKeyPath:@"periods"
                                            toKeyPath:@"periods"
                                          withMapping:[CFFPeriod mapping]];
[periodMapping addPropertyMapping:periodsMapping];

// Period.
[periodMapping addAttributeMappingsFromDictionary:@{@"open": @"open", @"close": @"close"}];

All the mapping operations run smoothly, but although the logs show the matches for all the entities, in the end I get a container.infos[0].value object with 0 key/value pairs. The logs actually says:

restkit.object_mapping:RKMappingOperation.m:887
Mapped relationship object from keyPath 'value' to 'value'.
Value: <CFFOpeningTimes: 0x796eb6d0>

The weird thing is that if a different dynamic mapping is applied for value (such as [CFFSurvey mapping]) the mapped object contains the correct data. I also tried to debug the execution of the mapping operations, and as far as I could see, the mapNestedObject:toObject:parent:withRelationshipMapping:metadataList: method in RKMappingOperation.m - line 883 seems not to copy the mapped nested object to the destination object returned.

I'm really going mad about this. Any ideas about where the problem might be?

1 个答案:

答案 0 :(得分:0)

好的我发现了问题所在。正如我所指出的,NSMutableDictionary的{​​{1}}类型显然不符合可能映射到它上面的通用结构。

所以我只是用更通用的CFFInfo.value替换了那个类型。

NSObject<NSCopying>

我添加了@interface CFFInfo : NSObject @property (nonatomic, retain) NSNumber *identifier; @property (nonatomic, retain) NSString *name; @property (nonatomic, retain) NSNumber *type; @property (nonatomic, retain) NSObject<NSCopying> *value; @property (nonatomic, retain) NSDate *created; @end 协议并为NSCopying类和级联中使用的类实现了copyWithZone:方法,以确保正确复制所有数据,以防副本那个对象是必需的。