3

I'm struggling to find a way to map some JSON into RestKit. This is an example of what I'm looking at:

       "results":{
           "Test1":[
              {
                 "id":1,
                 "name":"Test 1 here.",
                 "language":"English",
                 "type: "Test1"
              }
           ],
           "Test2":[
              {
                 "id":3,
                 "name":"Another test 2",
                 "language":"English",
                 "type":"Test2"
              },
              {
                 "id":8,
                 "name":"More test 2",
                 "language":"English",
                 "type":"Test2"
              },
              {
                 "id":49,
                 "name":"foo",
                 "language":"English",
                 "type":"Test2"
              }
           ]
        }

Ideally, the JSON wouldn't include the extra redundant layer of "type" as a key, but such is life.

I'd want RestKit to return 4 objects under "results" of type:

@interface Test : NSObject

@property (nonatomic, copy) NSNumber *testId;
@property (nonatomic, copy) NSString *testName;
@property (nonatomic, copy) NSString *testLanguage;
@property (nonatomic, copy) NSString *testType;

I've tried different combinations of mappings such as:

RKObjectMapping *testMapping = [RKObjectMapping mappingForClass:[Test class]];
testMapping.forceCollectionMapping = YES;
[testMapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"testType"];
[testMapping addAttributeMappingsFromDictionary:@{
                                                      @"(testType).id": @"testId",
                                                      @"(testType).name": @"testName",
                                                      @"(testType).language": @"testLanguage",
                                                      }];

but it still fails because its not a single object under the "type" JSON key - it is an array of Test objects.

Is there a way to represent this mapping in RestKit? Or if not, be able to override some callback functions so I can make it work? Unfortunately, I can't change the JSON data coming from the server

1 Answers1

3

I'd say your best bet is to create a response descriptor with a key path of @"results" and a dynamic mapping. That mapping would return a mapping which maps into an NSDictionary and which has a number of relationships defined. This dictionary is just a container to facilitate the other mappings (the relationships).

The relationships are created by iterating the keys of the representation provided to the dynamic mapping and creating one relationship per iteration, using the testMapping, but without the addAttributeMappingFromKeyOfRepresentationToAttribute as you can now use direct attribute access (and the inner type attribute).

Using setObjectMappingForRepresentationBlock:, your block is provided with the representation, which in your case is an NSDictionary of the deserialised JSON. Inside the block you create a mapping just as you usually would, but with the contents based on the keys in the dictionary.


RKObjectMapping *testMapping = [RKObjectMapping mappingForClass:[Test class]];
[testMapping addAttributeMappingsFromDictionary:@{ @"id": @"testId", @"name": @"testName" }];

[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
    RKObjectMapping *testListMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
    for (NSString *key in representation) {
        [testListMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeypath:key toKeyPath:key withMapping:testMapping];
    }

    return testListMapping;
}];
Wain
  • 118,658
  • 15
  • 128
  • 151
  • Thanks for your response on this. I'm not quite sure I follow you, though. I'm relatively new to RestKit, so I'm not sure how to implement what you're talking about -- Is there anything you can recommend looking at as an example of this? – ClemsonJeeper Mar 30 '14 at 19:02
  • I've added a little more info. There is no guide for this type of mapping AFAIK. Give it a try, show your code if you have issues. – Wain Mar 30 '14 at 21:07
  • Ahh, I got it now. That was the part I was missing. Thanks! – ClemsonJeeper Mar 31 '14 at 13:48
  • I got sidetracked on another project, and am back on this one. So, I thought I had it figured out with your help, but still am having issues. I understand what you mean by creating the dynamic mapping and getting the representation in an NSDictionary - I have that working. The part I don't get is how to specify a dynamic number of destination keyPaths. In my example above, we have two NSArrays of Test objects (one for Test1 and one for Test2). How do I specify this in a class and map to it (in my representation block) dynamically? – ClemsonJeeper Apr 28 '14 at 18:28
  • I'd return a mapping to a mutable array with a relationship for each of the keys, so the mapping result would contain a dictionary of arrays of custom objects – Wain Apr 28 '14 at 18:49
  • That's the part I'm having issues with. Since I have multiple "fromKeyPath" (one per key) and a single "toKeyPath" it complains, regardless of the type of the ToKeyPath (Mutable or Immutable). I think I must be missing a critical part of your explanation. Maybe some code will help? Again thanks much for the time you've spent on this. http://pastebin.com/ZFuzHRJD – ClemsonJeeper Apr 28 '14 at 19:24
  • The from and to key paths should be the same, then you can enumerate the keys in the resulting dictionary (in the mapping response) – Wain Apr 28 '14 at 19:30
  • toKeyPath does not exist in the class, though. If you look at my example, the foreach would iterate through "Test1" and "Test2" keys. There is no "Test1" member of TestList since we don't know about this key until runtime. So fromKeyPath and toKeyPath cannot be the same. In fact I changed toKeyPath to key (from @"tests") and I got the expected error: 2014-04-28 15:31:43.056 xctest[78217:4003] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key Test1.' – ClemsonJeeper Apr 28 '14 at 19:35
  • The to key path is to create a key in the dictionary. So, you have a mapping to dictionary, it has 2 relationships (or, one for each from key you have coming in) and each relationship uses your testMapping. testMapping should no longer have `addAttributeMappingFromKeyOfRepresentationToAttribute` – Wain Apr 28 '14 at 19:37
  • What is `TestList`? You don't need a new class, your result should be an `NSMutableDictionary` containing a number of `NSMutableArray`s – Wain Apr 28 '14 at 19:39
  • See http://pastebin.com/ZFuzHRJD It is currently what I am working with which is obviously incorrect. I'm having difficulty wrapping my brain around how this needs to be defined, so I think I'm going about it the absolute wrong way. Can you look at the pastebin and make some recommendations? – ClemsonJeeper Apr 28 '14 at 19:44
  • sorry to keep bugging you. Did you have a chance to look at the pastebin and perhaps recommend a path forward? If not, thanks for your help up to this point, I certainly appreciate it! – ClemsonJeeper Apr 29 '14 at 19:04
  • Odd, it didn't show up as a notification for me. Either way, I finally see what you were talking about. I tested it out and it totally works - thanks again for all your time! – ClemsonJeeper Apr 29 '14 at 19:58