Restkit 0.20 Dynamic Nested Mapping - restkit-0.20

I am using Restkit 0.20 to map and store objects in core data. For basic mappings it works well, but it doesnt work for nested dynamic mappings. This is my example response:
[
{
"actor": {
"id": 7,
"first_name": "Murat",
"last_name": "Akbal"
},
"target": {
"content_type": "dress",
"type": {
"id": 1,
"name": "leggings",
"kind": "bottom"
},
"style": {
"id": 1,
"name": "sport"
},
"full_name": "Murat - sport leggings",
"id": 19,
}
},
{
"actor": {
"id": 7,
"first_name": "Murat",
"last_name": "Akbal"
},
"target": {
"content_type": "wardrobe",
"style": {
"id": 1,
"name": "sport"
},
"id": 38,
"season": "spring",
"name": "asdasd"
}
}
]
I use following entity mapping to map response:
//create entity mapping
RKEntityMapping *objectMapping = [RKEntityMapping mappingForEntityForName:#"Action"
inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore];
//create dynamic mapping
RKDynamicMapping *dynamicMapping = [RKDynamicMapping new];
//dressMapping and wardrobeMapping predefined mappings, they work well when mapping the wardobe and dress alone.
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKey:#"content_type"] isEqualToString:#"wardrobe"]) {
return wardrobeMapping;
} else if ([[representation valueForKey:#"content_type"] isEqualToString:#"dress"]) {
return dressMapping;
}
return nil;
}];
[objectMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"target" toKeyPath:#"target" withMapping:dynamicMapping]];
//maps actor
[objectMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"actor" toKeyPath:#"actor" withMapping:actorMapping]];
And lastly, I register the objectMapping as response descriptor:
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:objectMapping
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
This doesn't work for my example response, gives the following error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Wardrobe 0x110e1a90> valueForUndefinedKey:]: the entity Wardrobe is not key value coding-compliant for the key "type".'
It asks "type" key which does not exist in wardrobe, but exists for dress. Somehow, it tries to map wardrobe with dress mapping. Do you have any idea about that issue?
fyi, type of target in coredata is transformable.

I found the problem. Actually it mapped response successfully, but when deleteLocalObjectsMissingFromMappingResult is preforming it throws an exception from this line:
id managedObjects = event.keyPath ? [objectsAtRoot valueForKeyPath:event.keyPath] : objectsAtRoot;
it seeks keyPath for all object at mapping results instead of each individual object. So in my situation it cannot find the "type" key for wardrobe because it does not exist. As a basic solution, I surrounded the code block with try-catch

Related

dynamic object mapping for nested json in Restkit 0.20

Can anyone please help me map this json object in restkit 0.20 ?
"feeds" : [
{
"id": 32131354,
"caption": "Blah Blah Blah.",
"story" : "blah blah someone blah blah someone else",
"story_tags": {
"0": [
{
"id": 21231654,
"name": "Someone",
"offset": 0,
"length": 25,
"type": "page"
}
],
"33": [
{
"id": 3213212313,
"name": "Someone else",
"offset": 33,
"length": 12,
"type": "page"
}
]
},
}
{
...
}
]
I would really appreciate any help. This is what am doing right now:
RKEntityMapping *feedsMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([FacebookFeed class]) inManagedObjectStore:managedObjectStore];
feedsMapping.identificationAttributes = #[#"id"];
[feedsMapping addAttributeMappingsFromDictionary:#{
#"caption" : #"caption",
#"id" : #"id",
#"story" : #"story",
}];
and this is for relationship mapping
RKEntityMapping *facebookFeedStoryTagsMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([FacebookFeedStoryTag class]) inManagedObjectStore:managedObjectStore];
facebookFeedStoryTagsMapping.identificationAttributes = #[#"id"];
[facebookFeedStoryTagsMapping addAttributeMappingToKeyOfRepresentationFromAttribute:#"offset"];
[facebookFeedStoryTagsMapping addAttributeMappingsFromDictionary:#{
#"(offset).id" : #"id",
#"(offset).length" : #"length",
#"(offset).name" : #"name",
#"(offset).type" : #"type",
}];
[feedsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"story_tags" toKeyPath:#"story_tags" withMapping:facebookFeedStoryTagsMapping]];
but i am getting the following error:
restkit.object_mapping:RKMappingOperation.m:745 Key path 'story_tags' yielded collection containing another collection rather than a collection of objects: (
(
{
id = 31314654654;
length = 25;
name = "Someone";
offset = 0;
type = page;
}
)
)
The JSON "0": [ presents an issue because we really want a dictionary in there (that is how your mapping is written), but we actually have an array. This is the source and meaning of the error you see.
Ideally you would change the JSON...
As it stands your FacebookFeedStoryTag would need to map the entire array (and the dictionary it contains) into a transformable attribute. This is because the mapping can not handle the nested array.
You could make this transformable attribute a transient and implement a custom setter / willSave method to take the array and extract the dictionary contents. As the key names match this could be simply done using setValuesForKeysWithDictionary: (though you would need to handle the offset key).

Mapping With Restkit

I am mapping with simple json response,
{
"town": "ABC",
"website": "http://xyz.com",
"fee": "50.00",
"event_id": 32,
"images": [
{
"large": "1.jpg",
"thumbnail": "2.jpg"
},
{
"large": "1.jpg",
"thumbnail": "2.jpg"
},
{
"large": "1.jpg",
"thumbnail": "2.jpg"
}
]
}
and my mapping function look like this
RKManagedObjectMapping *eventMapping = [RKManagedObjectMapping mappingForClass:[Events class] inManagedObjectStore:manager.objectStore];
[eventMapping mapKeyPath:#"town" toAttribute:#"town"];
[eventMapping mapKeyPath:#"website" toAttribute:#"website"];
[eventMapping mapKeyPath:#"fee" toAttribute:#"fee"];
[eventMapping mapKeyPath:#"event_id" toAttribute:#"event_id"];
eventMapping.primaryKeyAttribute = #"event_id";
[manager.mappingProvider setMapping:eventsMapping forKeyPath:#""];
RKManagedObjectMapping *imagesMapping = [RKManagedObjectMapping mappingForClass:[Images class] inManagedObjectStore:manager.objectStore];
[imagesMapping mapKeyPath:#"large" toAttribute:#"large"];
[imagesMapping mapKeyPath:#"thumbnail" toAttribute:#"thumbnail"];
[eventsMapping mapKeyPath:#"images" toRelationship:#"images" withMapping:imagesMapping];
But i am not to make a relationship successfully. My Db is always showing empty relationship b/w images and events. i tried connectrelationship, but i think i need primary key for images table to make a connection. can anyone guide me to correct way of doing this.
Do i need to add primary key for Images table or is it possible to access the 'event_id' from parent table (event) and use it for making relations.
Try this
//Note this is Restkit 0.2
[eventMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"images" toKeyPath:#"images" withMapping: imagesMapping]];

RestKit: JSON mapping doesn't work Objective-c

I use the RestKit framework to connect to a rest webservice.
How should the mapping look like if i want to have the resultlistEntries.
I've got a JSON-response like this:
{
"resultlist.resultlist": {
"paging": {
"numberOfHits": 69978,
"numberOfPages": 3499,
"pageNumber": 1,
"pageSize": 20
},
"resultlistEntries": [
{
"#numberOfHits": "69978",
"#realEstateType": "7",
"resultlistEntry": [
{
"#creation": "2013-01-15T16:36:00.000+01:00",
"resultlist.realEstate": {
"#id": "68014527",
"#xsi.type": "search:Office",
"calculatedPrice": {
"currency": "EUR",
"marketingType": "RENT_PER_SQM",
"priceIntervalType": "MONTH",
"value": 4.5
},
"commercializationType": "RENT",
"courtage": {
"hasCourtage": "YES"
},
"floorplan": "false",
"netFloorSpace": 155,
"price": {
"currency": "EUR",
"marketingType": "RENT",
"priceIntervalType": "MONTH",
"value": 697.5
},
"totalFloorSpace": 155
}
}
]
}
]
}
}
and the following RestKit Code
NSString *pathPattern = #"resultlist.resultEntries";
NSString *keyPath = #"resultEntry";
RKResponseDescriptor *responseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:mapping
pathPattern:pathPattern
keyPath:keyPath
statusCodes:statusCodes];
the result is the following:
2013-01-15 16:49:00.102 RestKit_final[31840:c07]
Failed with error: No response descriptors match the response loaded.
Whats wrong ?
pathPattern is used to specify an endpoint where the response descriptor will be used. For example, #"/hotels/premium" as a pathPattern will mean that the response descriptor will not apply if you call #"/hotels/cheap". You can set this to nil, but I always like to keep my response descriptors as specific as possible.
The keypath for the resultlistEntry array in the JSON response would technically be resultlist.resultlist.resultlistEntries, but you may have some issues with the resultlist.resultlist part. I would suggest changing your API to return simply resultList then your required keypath will be resultlist.resultlistEntries.

RestKit - Hydrate array of foreign keys

I have the following JSON:
{
"users": [
{"id": "1", "name": "John Doe"},
{"id": "2", "name": "Bill Nye"}
],
"groups": [
{"id": "1", "name": "Group1", "users": ["1", "2"]},
{"id": "2", "name": "Group2", "users": ["1"]}
]
}
...and a Core Data model with User and Group objects. The group object has a to-many relationship (NSSet) to users.
I have found the following thread that seems to indicate that this is possible, but contains no explanation of how such a mapping is to be performed:
https://github.com/RestKit/RestKit/issues/284
How do I perform this mapping such that each Group's "users" relationship is properly connected?
Note: I have mappings set up that correctly map the JSON users and groups to their respective Core Data objects. However, each group's "users" NSSet is empty.
So, I figured this out using RestKit 0.20(pre2).
JSON needed to be changed to the following (note the attribute names in the group's users array):
{
"users": [
{"id": "1", "name": "John Doe"},
{"id": "2", "name": "Bill Nye"}
],
"groups": [
{"id": "1", "name": "Group1", "users": [{"id" : "1"}, {"id" : "2"}]},
{"id": "2", "name": "Group2", "users": [{"id" : "1"}]}
]
}
Then, the following mappings:
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:managedObjectStore];
userMapping.identificationAttributes = #[#"id"];
[userMapping addAttributeMappingsFromArray:#[#"id", #"name"]];
RKEntityMapping *groupMapping = [RKEntityMapping mappingForEntityForName:#"Group" inManagedObjectStore:managedObjectStore];
groupMapping.identificationAttributes = #[#"id"];
[groupMapping addAttributeMappingsFromArray:#[#"id", #"name"]];
[groupMapping addRelationshipMappingWithSourceKeyPath:#"users" mapping:userMapping];
And finally, the following responseDescriptors:
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:classMapping pathPattern:#"/api/allthejson" keyPath:#"users" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKResponseDescriptor *groupResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:classMapping pathPattern:#"/api/allthejson" keyPath:#"groups" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptorsArray:#[userResponseDescriptor, groupResponseDescriptor]];
Then get your objects using RKObjectManager's getObjectsAtPath:parameters:success:failure method and your done!
RestKit has many issues especially when it comes to modeling relationships. Debugging the mappings can be daunting.
Here is some code that deals with what you describe without RestKit.
NSArray *userArray;
// an array populated with NSManagedObjects
// correctly converted from JSON to the User entity
NSArray *groups = [jsonObject objectForKey:#"groups"];
for (NSDictionary *d in groups) {
Group *g = [NSEntityDescription insertNewObjectForEntityForName:#"Group"
inManagedObjectContext:_managedObjectContext];
g.id = #([d[#"id"] intValue]);
g.name = d[#"name"];
NSArray *users = d[#"users"];
for (NSString *s in users) {
User *u = [[userArray filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"id = %#", #([s intValue])]]
objectAtIndex:0];
[g addUsersObject:u];
}
}
// save

How do you map a relationship by a primary key in Restkit?

Haven't been able to find an up to date example on how to map a relationship like the one below.
{
"seats": [
{
"number": "2A",
"user_id": 1
},
{
"number": "4E",
"user_id": 2
}
],
"users": [
{
"id": 1,
"name": "Foo"
},
{
"id": 2,
"name": "Bar"
}
]
}
using RestKit (0.9.3) to a model of
User
NSUInteger id
NSString* name
Seat
NSString* number
User* user;
I don't believe RestKit can do exactly what you want out of the box.
Your best bet might be to modify your data to be more RestKit friendly. You could do this via the objectLoader:willMapData: delegate method:
http://restkit.org/api/0.9.3/Protocols/RKObjectLoaderDelegate.html#//api/name/objectLoader:willMapData: