dynamic object mapping for nested json in Restkit 0.20 - objective-c

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).

Related

Restkit 0.20 Dynamic Nested Mapping

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

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 - 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

IPhone Google JSON Geocoing Parsing

I am unsure how to parse nested JSON output. I am using Googles JSON geocoding and trying to extract the coordinates. I am thinking I need another level of parsing. My code that does not work:
hostStr = [hostStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *hostURL = [NSURL URLWithString:hostStr];
NSString *jsonString = [[NSString alloc] initWithContentsOfURL:hostURL];
self.jsonArray = [jsonString JSONValue];
NSDictionary *coordinates = [self.jsonArray objectForKey:#"id:p1"];
NSLog(coordinates);
NSString *point = [coordinates objectForKey:#"Point"];
NSLog(point);
The JSON:
{
"name": "11111 WASHINGTON AVE # 116 SAN PEDRO, CA 91111",
"Status": {
"code": 200,
"request": "geocode"
},
"Placemark": [ {
"id": "p1",
"address": "11111 Washington Ave, San Pedro, CA 91111, USA",
"AddressDetails": {
"Accuracy" : 8,
"Country" : {
"AdministrativeArea" : {
"AdministrativeAreaName" : "CA",
"Locality" : {
"LocalityName" : "San Pedro",
"PostalCode" : {
"PostalCodeNumber" : "91111"
},
"Thoroughfare" : {
"ThoroughfareName" : "Washington Ave"
}
}
},
"CountryName" : "USA",
"CountryNameCode" : "US"
}
},
"ExtendedData": {
"LatLonBox": {
"north": 37.7026153,
"south": 37.6999173,
"east": -122.1393925,
"west": -122.1420904
}
},
"Point": {
"coordinates": [ -122.1407313, 50.7012704, 0 ]
}
} ]
}
It looks like you're unclear on how to reach into nested objects in the parsed JSON. I assume you're using JBJson (formerly Json-Framework) to get that JSONValue category on NSString, yes? If so, it's parsed the whole deal, you just have to walk it.
The first helpful this online json visualizer: http://jsonviewer.stack.hu/
Paste your raw JSON into that to see the whole tree structure of it.
Then, you should understand your'e going to to get an NSDictionary that has NSDictionaries and NSArrays in its values. So it's not "id: p1" you're digging into. If you want those coordinates, the coordinates array is at the path Placemark[0].Point.coordinates.
It's going to look more like:
NSArray *placemarks = [self.jsonArray objectForKey:#"Placemark"];
NSDictionary *mark = [placemarks objectAtIndex:0];
NSDictionary *point = [mark objectForKey:#"Point"];
NSArray *coordinates = [point objectForKey:#"coordinates"];
A clever person who has worked in Obj-c more recently than I might be able to walk that with an keypath or something, but I did it this way to show you the nested objects you're really navigating here.

How to parse the geolocation json from google for lng and lat in objective-c

I'm creating a web request to pull lng and lat data based on zip (using the following url)
http://maps.google.com/maps/geo?q=50266
Doing so I get back the following json
{
"name": "50266",
"Status": {
"code": 200,
"request": "geocode"
},
"Placemark": [ {
"id": "p1",
"address": "West Des Moines, IA 50266, USA",
"AddressDetails": {
"Accuracy" : 5,
"Country" : {
"AdministrativeArea" : {
"AdministrativeAreaName" : "IA",
"Locality" : {
"LocalityName" : "West Des Moines",
"PostalCode" : {
"PostalCodeNumber" : "50266"
}
}
},
"CountryName" : "USA",
"CountryNameCode" : "US"
}
},
"ExtendedData": {
"LatLonBox": {
"north": 41.6005010,
"south": 41.5254700,
"east": -93.7347000,
"west": -93.8435030
}
},
"Point": {
"coordinates": [ -93.7353858, 41.5998115, 0 ]
}
} ]
}
What I'm trying to pull from this is simply the coordinates section. Here is my current attempt that throws and exception when I hit the last line shown below
//after I get the response I turn it into json and this is working
NSArray* json = [items JSONValue];
NSString* coords = [json objectForKey:#"Placemark.Point.coordinates"];
What I'm I missing here for a quick pull of the coordinates?
The overall value return is not an array, it's a dictionary. Note that the first character is {. If it were an array, it would be [.
NSDictionary * json = [string JSONValue];
Now, you want the stuff under the "Placemarks" key. Note that this returns an array, since the character after the "Placemarks" key (and colon) is a [.
NSArray * placemarks = [json objectForKey:#"Placemark"];
From this array, you want the first element, which is another NSDictionary:
NSDictionary *firstPlacemark = [placemarks objectAtIndex:0];
From this dictionary, you want the dictionary under the key "Point":
NSDictionary *point = [firstPlacemark objectForKey:#"Point"];
From this dictionary, you want the array under the key "coordinates":
NSArray * coordinates = [point objectForKey:#"coordinates"];
At this point, you have the array that contains 3 NSNumber objects. Voilá!
For the advanced user, you can probably use key-value coding to get at it:
NSArray * coordinates = [json valueForKeyPath:#"Placemark[0].Point.coordinates"];
Though I wouldn't recommend that unless you clearly understand what's going on there.
That does not work. Never mind! :)
Keep in mind that "[ -93.7353858, 41.5998115, 0 ]" is not a string