Question

Working with RestKit, I'm trying to map the following JSON.

{
  "userID": 1,
  "collections": [
    {
      "collectionID": 120,
      "friends": [
        {
          "friendID": 6,
          "orders": [
            {
              "orderID": 1,
              "name": "Small"
            }
          ]
        }
      ]
    },
    {
      "collectionID": 123,
      "friends": [
        {
          "friendID": 6,
          "orders": [
            {
              "orderID": 2,
              "name": "Medium"
            }
          ]
        }
      ]
    }
  ]
}

I'm utilising RestKit and MagicalRecord - setup code, mapping and relationships below

NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"Model" ofType:@"momd"]];
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"db.sqlite"];

[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:nil];
[managedObjectStore createManagedObjectContexts];

// Configure MagicalRecord to use RestKit's Core Data stack
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:managedObjectStore.persistentStoreCoordinator];
[NSManagedObjectContext MR_setRootSavingContext:managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:managedObjectStore.mainQueueManagedObjectContext];

RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://127.0.0.1:3500"]];
objectManager.managedObjectStore = managedObjectStore;

RKEntityMapping *appMapping = [RKEntityMapping mappingForEntityForName:@"App" inManagedObjectStore:managedObjectStore];
appMapping.identificationAttributes = @[ @"userID" ];

RKEntityMapping *collectionMapping = [RKEntityMapping mappingForEntityForName:@"Collection" inManagedObjectStore:managedObjectStore];
collectionMapping.identificationAttributes = @[ @"collectionID" ];
[collectionMapping addAttributeMappingsFromArray:@[@"collectionID"]];

RKEntityMapping *friendMapping = [RKEntityMapping mappingForEntityForName:@"Friend" inManagedObjectStore:managedObjectStore];
friendMapping.identificationAttributes = @[ @"friendID" ];
[friendMapping addAttributeMappingsFromArray:@[@"friendID"]];

RKEntityMapping *orderMapping = [RKEntityMapping mappingForEntityForName:@"Order" inManagedObjectStore:managedObjectStore];
orderMapping.identificationAttributes = @[ @"orderID" ];
[orderMapping addAttributeMappingsFromArray:@[@"orderID", @"name"]];


RKRelationshipMapping *appCollectionRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:@"collections" toKeyPath:@"collections" withMapping:collectionMapping];
[appMapping addPropertyMapping:appCollectionRelationship];

RKRelationshipMapping *collectionFriendRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:@"friends" toKeyPath:@"friends" withMapping:friendMapping];
[collectionMapping addPropertyMapping:collectionFriendRelationship];

RKRelationshipMapping *friendsOrderRelationship = [RKRelationshipMapping relationshipMappingFromKeyPath:@"orders" toKeyPath:@"orders" withMapping:orderMapping];
[friendsOrderRelationship setAssignmentPolicy:RKUnionAssignmentPolicy];
[friendMapping addPropertyMapping:friendsOrderRelationship];

Then, querying the /test route on my API (which outputs the JSON block above) with the below console out...

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:appMapping method:RKRequestMethodAny pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];

[objectManager getObjectsAtPath:@"/test" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {

    App *app = [mappingResult firstObject];
    for (Collection *collection in app.collections) {
        NSLog(@"collectionID = %@", collection.collectionID);
        for (Friend *friend in collection.friends) {
            NSLog(@"friendID = %@", friend.friendID);
            for (Order *order in friend.orders) {
                NSLog(@"Name = %@", order.name);
            }
        }
    }
} failure:^(RKObjectRequestOperation *operation, NSError *error) {}];

Provides the following output - notice both Name's are Medium, not one Small and the other Medium.

collectionID = 120
friendID = 6
Name = Medium
Name = Small
collectionID = 123
friendID = 6
Name = Medium
Name = Small

Why would this be happening? I'm going to guess that it's because both friend ID's are the same, even though they're under different collections...

Was it helpful?

Solution

This is because you have identificationAttributes = @[ @"friendID" ]; and the default relationship connection rule for relationships rather than replacement. So, the second mapping finds the friend created by the first mapping and replaces the order relationship contents.

You can't realistically change the unique identifier based on the JSON you show, but you might want to examine that (in relation to your object graph requirements).

Also / alternatively, see this answer for details of how to change the relationship assignment policy so that the original relationship contents aren't lost.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top