Hi,
I need help with testing the mappings of a JSON object with a nested array, like this:
{
"items":
[
{
"personId": 4,
"firstName": "John",
"lastName": "Doe"
},
{
"personId": 5,
"firstName": "Mike",
"lastName": "Moe"
}
]
}
In addition, the Person objects that should be mapped are NSManagedObjects:
@interface Person : NSManagedObject
@property (nonatomic, retain) NSNumber * personId;
@property (nonatomic, retain) NSString * firstName;
@property (nonatomic, retain) NSString * lastName;
@end
@implementation Person
@dynamic personId;
@dynamic firstName;
@dynamic lastName;
@end
I set up the mappings like this:
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[NSObject class] rootKeyPath:nil method:RKRequestMethodGET];
RKEntityMapping *responseMapping = [RKEntityMapping mappingForEntityForName:@"Person" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore];
[responseMapping addAttributeMappingsFromArray:@[ @"personId", @"firstName", @"lastName" ]];
responseMapping.identificationAttributes = @[ @"personId" ];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping method:RKRequestMethodGET pathPattern:@"findpersons" keyPath:@"items" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addRequestDescriptor:requestDescriptor];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
The API expects an empty GET request and returns a JSON response as shown above. This mapping itself actually works, the Person objects are successfully stored in CoreData.
What does not work, however, is this test for the mapping:
id parsedJSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"Person.json"];
RKEntityMapping *mapping = // ...
Person *person = [[RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext insertNewObjectForEntityForName:@"Person"];
NSArray *items = @[ person ];
// Alternatively also tried this with destinationObject person and items
RKMappingTest *test = [RKMappingTest testForMapping:mapping sourceObject:parsedJSON destinationObject:nil];
test.rootKeyPath = @"items";
test.managedObjectContext = [RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext;
// Alternatively also tried expectationWithSourceKeyPath:destinationKeyPath:evaluationBlock:
// However, it fails before the block is even evaluated.
[test evaluateExpectation:[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"items.personId" destinationKeyPath:@"personId" value:@4] error:&error];
[test addExpectation:[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"personId" destinationKeyPath:@"personId" value:@4]];
[test addExpectation:[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"firstName" destinationKeyPath:@"firstName" value:@"Max"]];
[test addExpectation:[RKPropertyMappingTestExpectation expectationWithSourceKeyPath:@"lastName" destinationKeyPath:@"lastName" value:@"Mustertrainer"]];
XCTAssertNoThrow([test verify]);
I stepped into the debugger and found that in RKMappingOperation, Line 760 in method applyAttributeMappings:
id value = (sourceKeyPath == nil) ? [sourceObject valueForKey:@"self"] : [sourceObject valueForKeyPath:sourceKeyPath];
the variable value is an NSArray containing two NSNumber objects, @4 and @5 (both personIds from the JSON object).
Further down the line in method transformValue:toValue:withPropertyMapping:error: the conversion from NSArray to NSNumber obviously fails.
I also found that during the normal execution (not in the unit tests, but in the actual app), the control flow goes through the RKMapp*er*Operation (not RKMapp*ing*Operation).
In mapRepresentationOrRepresentations:atKeyPath:usingMapping: it eventually finds out that the keyPath "items" is a collection of objects and splits it up for the RKMappingOperation to use. Thus the mapping works in the field.
Is there a solution to this problem? Even though mapping JSON arrays is a relatively common use-case, I was unable to find any solutions.
Regards,
Patrick