0

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?

lWhitmore
  • 613
  • 1
  • 5
  • 7

1 Answers1

0

Ok I found what the problem was. As I pointed out, the NSMutableDictionarytype for CFFInfo.value is clearly not compliant with the generic structures that may be mapped onto it.

So I just substituted that type with a more generic 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

I added the NSCopying protocol and implemented the copyWithZone: method for the CFFOpeningTimes class and the classes used in cascade, to assure all the data is copied correctly, in case a copy of that object is needed.

lWhitmore
  • 613
  • 1
  • 5
  • 7