RESTKIT request mapping for unknown keys - ios

{
"type": "at or leave",
"time": "XXXX",
"place_name": "Xx",
"place_id": "xx",
"place_attributes": {
"key": "val",
"key2": "val2",
},
"place_type": "public or private"
}
i want to post json like above.But placeAttributes dictionary in my app will be having unknown number of keys which are needed to be mapped with "place_attributes" in json above.

Created a NSDictionary* placeAttributes property in my request mapping class, mapped it to place_attributes key in json and directly assigned my dictionary to placeAttributes.
RKObjectMapping* map = [RKObjectMapping requestMapping];
[map addAttributeMappingsFromDictionary:#{
#"type":#"type",
#"time":#"time",
#"place_name":#"place_name",
#"place_id":#"place_id",
#"place_type":#"place_type",
#"placeAttributes":#"place_attributes"
}];

Related

RestKit mapping for JSOG API object

I'm trying to figure out how to realize a mapping of an API response into CoreData objects using RestKit. The API uses JSOG standard. Here is an example:
[
{
"#id": "1",
"name": "Sally",
"friend": {
"#id": "2",
"name": "Bob",
"friend": {
"#id": "3",
"name": "Fred",
"friend": { "#ref": "1" }
}
}
},
{ "#ref": "2" },
{ "#ref": "3" }
]
How would I create an RKEntityMapping for such a JSON? Mapping of simple attributes is trivial, the question is how to setup the relationships here so they work with #ref, also, when the top level user object contains the #ref only.
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"Question"
inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore];
[userMapping addAttributeMappingsFromDictionary:#
{
#"#id" : #"identifier",
#"name" : #"name"
}];
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"friend"
toKeyPath:#"friend"
withMapping:userMapping]];
My guess is that i could use code below to handle the #ref inside of an object:
[userMapping addConnectionForRelationship:#"friend" connectedBy:#{#"#ref": #"#id"}];
but is it correct?
How would I map either a full object or just a reference to an already-provided object (via #ref) to actual Core Data objects?
How can I map the top elements of the JSON (being a list of User objects) to an actual list of User entities?
Add a relationship using the mapping itself, so it drills recursively. Use foreign key mapping for the refs.
Looking at this again following your comment it may actually be a good candidate for a dynamic mapping instead of foreign key. This would mean creating a new mapping which just uses #"#ref" : #"identifier" for the connections and a dynamic mapping which looks at the keys and decides wether to use this connection mapping or the full userMapping.
Note that both of these mappings need to specify that identifier is the unique key of the object and you should use a memory cache to allow the existing objects to be found instead of creating duplicates.

Using RKDynamicMapping on Foursquare's Lists API to capture lists

I'm looking to get all of my Foursquare Lists into Core Data. I'd like to use Restkit to accomplish this. The structure of the /v2/users/self/lists response is:
"response": {
"lists": {
"count": 8,
"groups": [
{
"type": "created",
"name": "Lists You've Created",
"count": 6,
"items": [
{
"id": "13250/todos",
"name": "My to-do list", ...
}
{
"id": "13251/something",
"name": "Some List", ...
},
{
"id": "13252/somethingelse",
"name": "Some Other List", ...
}
]
},
{
"type": "followed",
"name": "Lists You've Saved",
"count": 1,
"items": [
{
"id": "5105e3cae4b0e721ca7b400a",
"name": "Portland's Best Coffee - 2012", ...
}
{
...
}
]
}
]
}
As you can see there are 2 lists under the keyPath response.lists.groups. Ultimately I'd like to merge those 2 lists into 1, but I'd be happy with getting 2 separate lists.
I've set up my mappings as follows:
RKEntityMapping* listMapping = [RKEntityMapping mappingForEntityForName:[FOFSList entityName]
inManagedObjectStore:objectManager.managedObjectStore];
[listMapping addAttributeMappingsFromDictionary:#{
#"id": #"listID",
#"title": #"name",
#"description": #"desc",
#"user": #"user",
#"following": #"following",
#"collaborative": #"collaborative",
#"canonicalUrl": #"canonicalUrl",
#"venueCount": #"venueCount",
#"visitedCount": #"visitedCount"
}];
RKDynamicMapping *dynamicMapping = [RKDynamicMapping new];
[listMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil
toKeyPath:#"items"
withMapping:dynamicMapping]];
RKResponseDescriptor *listResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:listMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"response.lists.groups"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:listResponseDescriptor];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKey:#"type"] isEqualToString:#"created"]) {
return listMapping;
} else if ([[representation valueForKey:#"type"] isEqualToString:#"followed"]) {
return listMapping;
}
return nil;
}];
listMapping.identificationAttributes = #[ #"listID" ];
I end up with an error:
* Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key propertyMappings.'
Am I supposed to be using RKDynamicMappings? Is there some trick that I'm missing for parsing a response that is styled like this?
For those that are interested, I got a little bit creative with the RKResponseDescriptor
RKResponseDescriptor *listResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:listMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"response.lists.groups.#distinctUnionOfArrays.items"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
See the collection operation #distinctUinionOfArrays was ultimately what got me what I needed. It makes a union of the 2 groups arrays, then I grab the items key from each of the objects in the union of the arrays.
At the moment the dynamic mapping is serving as a filter on the type. That's fine if it's what you want and is the correct way to achieve the filter. But, you're applying that mapping in the wrong way.
The dynamic mapping should be supplies as the mapping for the response descriptor. It analyses the incoming object and returns the appropriate mapping to apply.
You need a new, non-dynamic, mapping to handle the nested items.
Your other question about merging can't be handled during the mapping, but it could be done by adding a method to the destination class which is called with the mapped array and it merges with an existing array and pushes the merged result into the true instance variable. The mapping destination would be the method instead of the instance variable.

Restkit — How to map json objects with mutable key names

I have to map this structure, but I don't know how to identify the "br.myservice.com" part, because it changes and I cannot build a regular RKObjectMapping, as it uses fixed strings.
{ "objects": {
"br.myservice.com": {
"url": "http://br.myservice.com",
"name": "Brazil",
"flag": "br",
"countries": ["br"]
},
"us.myservice.com": {
"url": "http://us.myservice.com",
"name": "United States",
"flag": "us",
"countries": ["us"]
}
}
You need to use addAttributeMappingFromKeyOfRepresentationToAttribute: like so:
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[MyObject class]];
mapping.forceCollectionMapping = YES;
[mapping addAttributeMappingFromKeyOfRepresentationToAttribute:#"host"];
[mapping addAttributeMappingsFromDictionary:#{
#"(host).url": #"url",
#"(host).name": #"name",
... and so on ....
}];
Assuming that the keys are unknown to you at the time of writing the code:
You would need to create a dynamic mapping (RKDynamicMapping) with a block which inspects the received JSON dictionary and creates (and returns) a custom object mapping on the fly.
If you know the keys, just add them all and any that are appropriate will be used at runtime.

Restkit property mapping Failed Transformation error

this is what my sample code looks like:
{
"name": "Ahmad Mansour",
"subjects": [
{
"parent_subject_name": "Arabic",
"subject_name": "Shafahi",
"exams": [
{
"score": "30.00",
"exam_name": "Sa3i 1 "
},
{
"score": "50.00",
"exam_name": "sa3i 2"
},
{
"score": "100.00",
"exam_name": "First Semester Exam"
}
]
},
{
"parent_subject_name": "Arabic",
"subject_name": "Khati",
"exams": [
{
"score": "50.00",
"exam_name": "Sa3i 1 "
},
{
"score": "60.00",
"exam_name": "sa3i 2"
},
{
"score": "95.00",
"exam_name": "First Semester Exam"
}
]
},
for the subject entity.. my mapping works just fine:
RKEntityMapping *subjectEntityMapping = [RKEntityMapping mappingForEntityForName:kSubjectIdentity inManagedObjectStore:model.managedObjectStore];
[subjectEntityMapping addAttributeMappingsFromDictionary:#{ #"subject_name": #"name",
#"parent_subject_name" :#"parent_name"
}];
subjectEntityMapping.identificationAttributes = #[ #"name" ];
RKResponseDescriptor *studentResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:subjectEntityMapping pathPattern:kGradesPath keyPath:#"subjects" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[model.objectManager addResponseDescriptor:studentResponseDescriptor];
but when i do the exam score mapping.. things blow up:
RKEntityMapping *examEntityMapping = [RKEntityMapping mappingForEntityForName:kExamIdentity inManagedObjectStore:model.managedObjectStore];
[examEntityMapping addAttributeMappingsFromDictionary:#{ #"exams.exam_name": #"name" }];
examEntityMapping.identificationAttributes = #[ #"name" ];
RKResponseDescriptor *examResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:examEntityMapping pathPattern:kGradesPath keyPath:#"subjects" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[model.objectManager addResponseDescriptor:examResponseDescriptor];
i get the following error:
E restkit.object_mapping:RKMappingOperation.m:431 Failed transformation of value at keyPath 'exams.exam_name' to representation of type 'NSString': Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3002 "Failed transformation of value '(
"Sa3i 1 ",
"sa3i 2",
"First Semester Exam"
)' to NSString: none of the 2 value transformers consulted were successful." UserInfo=0x9a508a0 {detailedErrors=(
"Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3002 \"The given value is not already an instance of 'NSString'\" UserInfo=0x9a50800 {NSLocalizedDescription=The given value is not already an instance of 'NSString'}",
"Error Domain=org.restkit.RKValueTransformers.ErrorDomain Code=3000 \"Expected an `inputValue` of type `NSNull`, but got a `__NSArrayI`.\" UserInfo=0x9a50830 {NSLocalizedDescription=Expected an `inputValue` of type `NSNull`, but got a `__NSArrayI`.}"
I also tried this mapping, but i get the exact same error:
[examEntityMapping addAttributeMappingsFromDictionary:#{ #"exam_name": #"name" }];
examEntityMapping.identificationAttributes = #[ #"name" ];
RKResponseDescriptor *examResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:examEntityMapping pathPattern:kGradesPath keyPath:#"subjects.exams" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
ideas?
You shouldn't have a response descriptor for exams. It's nested data in your subject so it should be mapped using a relationship on the subject mapping.
Using the response descriptor doesn't work because you're trying to map into 2 arrays when the mapping only caters for one. Hence you get an error when RestKit tries to convert an array into a string.
Also, for your exam mapping you should probably specify multiple attributes for the unique identity as exam names are used repeatedly for different instances...

How to map a JSON array with RestKit

I have json string like this format :
[{"image":"/0001.jpg","link":"/index.php"},
{"image":"/0001.jpg","link":"/index.php"}]
it does not have a key in the top level.
[mapping mapKeyPath:#"image" toAttribute:#"image"];
mapping like this won't work , it give me the error:
restkit.object_mapping:RKObjectMapper.m:81 Adding mapping error: Could not find an object mapping for keyPath: ''
How to map this type of json ?
Use
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:myObject];
You should check "Mapping without KVC" section on Restkit Object Mapping Docs.
Here is an example from the docs:
[
{ "title": "RestKit Object Mapping Intro",
"body": "This article details how to use RestKit object mapping...",
"author": {
"name": "Blake Watters",
"email": "blake#restkit.org"
},
"publication_date": "7/4/2011"
}
]
And you map that like this:
// Our familiar articlesMapping from earlier
RKObjectMapping* articleMapping = [RKObjectMapping mappingForClass:[Article class]];
[articleMapping mapKeyPath:#"title" toAttribute:#"title"];
[articleMapping mapKeyPath:#"body" toAttribute:#"body"];
[articleMapping mapKeyPath:#"author" toAttribute:#"author"];
[articleMapping mapKeyPath:#"publication_date" toAttribute:#"publicationDate"];
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:articleMapping];
For me the solution was:
add this response descriptor, use the object inside de array
[manager addResponseDescriptor:[ObjectInsideTheArray getResponseDescriptor]];
and in the success response
NSArray *response = (NSArray *)[mappingResult array]; //instead of firstObject

Resources