Nikki
Nikki

Reputation: 434

RestKit Arbitrary Keys Object Mapping

I have a JSON that looks like this here:

 { 
  "key1":[ 
  { 
    "desc":"key1decs.", 
    "duration":50; 
  },{ 
    "desc":"", 
    "duration":90; 
  },{ 
    "desc":"Kurz vor...", 
    "duration":30; 
  } 
 ], 
 "key2":[ 
  { 
    "desc":"key2decs.", 
    "duration":50; 
  },{ 
    "desc":"blabla", 
    "duration":90; 
  } 
] 
} 

I am trying to do a mapping. But I'm stuck because of different unknown/arbitrary keys. I tried mapping without KVC, but it seems not to be working. Gives me error: Failed transformation of value at keyPath 'key1.desc'. No strategy for transforming from '__NSArrayI' to 'NSString'. and so on for every attribute. I saw on the web that mapping without KVC doesn't support arrays as inner structures. Plz, help. What is right approach here?

EDIT: ENtity:

@interface Sendung : NSObject

 @property (nonatomic, strong) NSString *description;
 @property (nonatomic, strong) NSNumber *duration;
 @property (nonatomic, strong) NSNumber *sendungkey;
@end;

Controller:

 RKObjectMapping* mapping = [RKObjectMapping mappingForClass:[Sendung class]];
 mapping.forceCollectionMapping = YES;
[mapping mapKeyOfNestedDictionaryToAttribute:@"sendungkey"];
[mapping mapKeyPath:@"(sendungkey).desc" toAttribute:@"description"];
[mapping mapKeyPath:@"(sendungkey).duration" toAttribute:@"duration"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:mapping ];
 mapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Sendung class] ];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url objectMapping:mapping delegate:self];

This is what I get when NSlogin result: ( (null), (null), (null), (null), (null) )

Upvotes: 4

Views: 3117

Answers (1)

mja
mja

Reputation: 5066

I believe you miss one 'level' of your mapping. If you take a closer look at your input, you have an dictionary (key1), which contents is not single 'Sendung' but an array of such objects. Thus, you can't map contents of the key1 directly to Sendung ivars, rather than create new class, eg SendungResult.

@interface SendungResult : NSObject
    @property (nonatomic, strong) NSString *keyName;
    @property (nonatomic, strong) NSArray *sendungs;
@end

and map the contents of key1 to SendungResult sendungs array using the relationship mapping you use now directly.

EDIT: I'm extending the answer as follows:

As i said earlier, you are trying to map a collection of objects into a single instance, so this can not work. Instead, you have to create a SendungResult that will hold an array of Sendung instances for one key. So in your example, you'll end up with two SendungResult instances (one will hold three Sendungs - those nested under "key1" and the second one just two, nested under "key2"). You can't map this kind of a structure into just once class, you have to nest them same way as your JSON is nested. The Sendung class is basically unchanged.

Now for the mapping. You need to define two mappings, one will map the contents of your root JSON into two SendungResult (one for each key) and the second mapping will map the inner part into individual Sendung instances.

We start with the inner mapping.

RKObjectMapping *sendungMapping = [RKObjectMapping mappingForClass:[Sendung class]];
[sendungMapping mapKeyPath:@"desc" toAttribute:@"desc"];
[sendungMapping mapKeyPath:@"duration" toAttribute:@"duration"];
[[[RKObjectManager sharedManager] mappingProvider] addObjectMapping:sendungMapping];

Nothing really interesting here, we just map the keypaths to your properties. Now for the outer part.

RKObjectMapping *resultMapping = [RKObjectMapping mappingForClass:[SendungResult class]];
[resultMapping setForceCollectionMapping:YES];
[resultMapping mapKeyOfNestedDictionaryToAttribute:@"keyName"];
[resultMapping mapKeyPath:@"(keyName)" toRelationship:@"sendungs" withMapping:sendungMapping];
[[[RKObjectManager sharedManager] mappingProvider] addObjectMapping:resultMapping];

We tell RestKit to map contents of the keyName keyPath to sendungs property found on SendungResult class with the mapping defined earlier.

Now we can fetch the data

[[RKObjectManager sharedManager]  loadObjectsAtResourcePath:@"test.js" objectMapping:resultMapping delegate:self];

The RestKit will create two SendungResult instances, each array contains the inner Sendung classes (3 and 2, respectively)

Note: I strongly suggest you to read RestKit Object Mapping documentation. Also, JSONLint found your JSON to be invalid, i had to remove the semicolons. So be sure to use valid json.

Upvotes: 5

Related Questions