RestKit: mapping of dynamic attribute (not a NSDictionary as in the example) - iphone

I'm using RestKit in my iOS project, and figured non-trivial problem and can't find solution for it.
I have a json:
[{
"name": "restkit",
"downloads": 2
},
{
"name": "restkit",
"rating": 10.0
}]
and data model: Model.h
#interface Model : NSObject
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSString * key;
#property (nonatomic, strong) NSString * value;
#end
Those JSON objects loaded in one array, and one of the attributes is a "dynamic" attribute.
After mapping performed in a RestKit I want to be able to have 2 records:
name: "restkit", key: "downloads", value: 2
name: "restkit", key: "rating", value: 10.0
Question: how to map JSON in the beginning into 2 NSObjects as shown in the example above?
This is how I initialize mapping using RestKit and firing requests:
// during app initialization I setup mappings:
RKObjectMapping *modelMapping = [RKObjectMapping mappingForClass:[Model class]];
[metricDataMapping mapKeyPath:#"name" toAttribute:#"name"];
...... -- something should go here to support that dynamic stuff
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:metricDataMapping];
// in the view controller when loading data
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader){
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass: [MetricData class]];
loader.onDidLoadObjects = ^(NSArray * objects){
self.dataArray = objects;
};
}];
The problem, is that keys "downloads" and "rating" are dynamic, and it could be any word. I need to parse that 'on-the-fly' and show in UI.
Restkit does support such behavior, but only for nested dictionaries.

You can access the dynamically named properties using key-value coding. After you had made an NSDictionary out of the JSON, do this:
[self setValue:[dictionary objectForKey:#"downloads"] forKey:#"downloads"];
etc.

I did use key-value validation on the model triggered by RestKit to set correct metric key and value to the generic key I want.

Related

JSON Dictionary to Core Data

I have a JSON response that I turn into a Dictionary like so:
NSError *error;
self.restKitResponseDict = [NSDictionary dictionaryWithDictionary:[NSJSONSerialization JSONObjectWithData:response.body options:0 error:&error]];
I have a core data class that has the following attributes/properties:
name
image_url
When I log the restKitResponseDict from above I see that image_url is listed as "image_url" like this:
name = Rock;
"image_url" = "http://f.cl.ly/items/122s3f1M1E1p432B211Q/catstronaut.jpg";
Is this why KVC is crashing on
[CoreDataClass setValuesForKeysWithDictionary:self.restKitResponseDict];
like this:
'[<CoreDataClass 0x14132c> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key image_url.'
Do the quotes matter? Should I ask my server guy to get rid of the underscore that's likely causing it?
Core Data Class:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataClass : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * image_url;
#end
#implementation CoreDataClass
#dynamic name;
#dynamic image_url;
#end
You are sending the method to the class object:
[CoreDataClass setValuesForKeysWithDictionary:self.restKitResponseDict];
when you probably want to send it to the actual CoreDataClass instance:
[coreDataClassObject setValuesForKeysWithDictionary:self.restKitResponseDict];
EDIT
what's the simplest way to init the object from the class? – Eric
It's a subclass of NSManagedObject, so you use the normal Core Data methods. One way to create a new object:
CoreDataClass *coreDataObject = [NSEntityDescription
insertNewObjectForEntityForName:#"YOUR_ENTITY_NAME"
inManagedObjectContext:managedObjectContext];
If you need basic information about using Core Data, see the Core Data Programming Guide: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/coredata/cdProgrammingGuide.html#//apple_ref/doc/uid/TP30001200-SW1
The NSLog() with the %# format uses the description method to print objects, and the description of a NSDictionary puts quotation marks around all keys and values that contain any special characters, such as the underscore. For example
NSDictionary *dict = #{
#"key1" : #"value_1",
#"key_2" : #"value2"
};
NSLog(#"dict=%#", dict);
produces
2012-08-25 18:15:33.553 test27[3416:c07] dict={
key1 = "value_1";
"key_2" = value2;
}
Therefore the key in your JSON dictionary does not really have quotation marks, and the underscore is probably not the cause of the error.
The error message indicates that the managed object does not have an image_url attribute, therefore you should check that.

Serializing a nested object when posting from iOS to Rails app

Hoping to get a little push in the right direction. I am having trouble getting a nested object to serialize properly when I POST to my rails app using RestKit. I have the following mappings:
RKObjectMapping *cartSerializationMapping = [RKObjectMapping mappingForClass:[TOCart class]];
[cartSerializationMapping mapKeyPath:#"place.placeID" toAttribute:#"order[external_id]"];
//map the line items serialization mapping
RKObjectMapping *lineItemSerializationMapping = [RKObjectMapping mappingForClass:[TOLineItem class]];
[lineItemSerializationMapping mapKeyPath:#"itemID" toAttribute:#"itemID"];
[lineItemSerializationMapping mapKeyPath:#"name" toAttribute:#"name"];
[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:lineItemSerializationMapping forClass:[TOLineItem class]];
//add relationship bw line items to TOLineItem
[cartSerializationMapping mapKeyPath:#"line_items" toRelationship:#"order[line_items]" withMapping:lineItemSerializationMapping serialize:YES];
[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:cartSerializationMapping forClass:[TOCart class]];
After posting to the server, serialization works for the parent object but not for the nested line_item object:
Started POST "/orders" for 127.0.0.1 at 2011-11-16 04:05:58 -0800
Processing by OrdersController#create as JSON
Parameters: {"order"=>{"line_items"=>["<TOLineItem: 0x8aafdb0>"], "external_id"=>"4ae8a535f964a52024b121e3"}}
I want the line_item to serialize to itemID and name etc...
Did I set my mappings incorrectly?
Thanks!
UPDATE:
My TOCart class:
#import <Foundation/Foundation.h>
#class TOPlace;
#interface TOCart : NSObject
{
NSNumber *cartID;
TOPlace *place; //post to external id
NSString *state;
NSMutableArray *line_items;
}
#property (nonatomic, retain) NSNumber *cartID;
#property (nonatomic, retain) TOPlace *place;
#property (nonatomic, retain) NSString *state;
#property (nonatomic, retain) NSMutableArray *line_items;
#end
I always define my mapping to map from API to entities & then create the serialization mapping with [myMappingFromApi inverseMapping] selector. You can find further details in my answer to somewhat different question, but definitely related: RestKit: How does one post an array of objects?.

RestKit: how to make a mapping for varying key names

I'm trying to make a dynamic mapping for a bit weird structured JSON.
I have "array mapped to object" sort of thins so that array indices
ake keys e.g.:
{
"0": {object},
"1": {another object},
"2": {yet another object},
...
}
All objects are of the same type so they can be parsed using the same
mapping, but how to deal with varying key names?
Check out the section on "Handling Dynamic Nesting Attributes" in the Object Mapping docs.
He walks through an example (copied here) with the JSON:
{ "blake": {
"email": "blake#restkit.org",
"favorite_animal": "Monkey"
}
}
Corresponding to the User class:
#interface User : NSObject
#property (nonatomic, retain) NSString* email
#property (nonatomic, retain) NSString* username;
#property (nonatomic, retain) NSString* favoriteAnimal;
#end
Which, you'll notice, the username property corresponds to the key of the JSON.
In order to map it, he uses a special parenthesis syntax to indicate that they key itself is a property:
RKObjectMapping* mapping = [RKObjectMapping mappingForClass:[User class] ];
[mapping mapKeyOfNestedDictionaryToAttribute:#"username"];
[mapping mapFromKeyPath:#"(username).email" toAttribute:"email"];
[mapping mapFromKeyPath:#"(username).favorite_animal" toAttribute:"favoriteAnimal"];
Hope this helps!

Convert 2D NSMutableArray in JSON format - JSONKit

I have the following array
NSMutableArray* answers;
Each element of answers is itself an array of objects.
I need to convert the above 2D array into appropriate JSON format (using the JSONKit framework), so that it can be passed to a php application and decoded thereafter...
The individual objects have the following structure:
#interface Answer : NSObject {
//NSString* answerId;
NSString* answer;
NSString* questionId;
}
//#property (nonatomic, retain) NSString* answerId;
#property (nonatomic, retain) NSString* answer;
#property (nonatomic, retain) NSString* questionId;
#end
That is that each element of answers is an array of Answer Objects. essentially what I need is to encode the relevant data in each answer object into JSON format using the JSONKit framework so that it can be posted to a php app and decoded....
Essentially I need somthing of the form:
{{"answer":"1","questionId":"1"}, {{"answer":"5","questionId":"2"},......}
The JSONKit, like all other JSON frameworks, does not play well with custom objects. To that end, you need to iterate through your object and put them into objects that the JSONKit can understand (NSArrays and NSDictionaries). Something like this:
NSMutableArray *jAnswers = [[[NSMutableArray alloc] init] autorelease];
for(Answer *answ in answers)
{
NSMutableDictionary *jAnswer = [[[NSMutableDictionary alloc] init] autorelease];
[jAnswer addObject: answ.answer forKey: #"answer"];
[jAnswer addObject: answ.questionId forKey: #"questionId"];
[jAnswers addObject: jAnswer];
}
NSString *jAnswersJSONFormat = [jAnswers JSONString];
would give you:
[{"answer": "1", "questionId": "1"}, {"answer": "5", "questionId": "2"}, ...]
JSONKit does seem to offer both a delegate-based and a block-based method for serializing unsupported object types. My guess, not having used the framework, is that you call one of those versions of the serialization methods and pass a delegate/selector pair or a block which returns a JSON-serializable object in place of an unsupported object type.
You'll want one of these category methods on NSArray:
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
- (NSString *)JSONStringWithOptions:(JKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;

Restkit OM2 relationship mapping

Im using Restkit OM2 to take in a json and map to objects on iphone.
Im currently confused on how to structure the mappings and could do with some help.
Below is an example json file
{
-magic_verbs: [
-{
lemma: "work"
position: 5
score: "0.75"
value: "working"
}
-{
lemma: "head"
position: 0
score: "0.75"
value: "heading"
}
],
magic_advs: [
-{
lemma: "not"
position: 2
score: "0.6"
value: "not"
}
-{
lemma: "just"
position: 2
score: "0.6"
value: "just"
}
]
}
i only need the lemma and value fields from each of these. so for example the verb class contains
#interface Verbs : NSManagedObject {
}
#property (nonatomic,retain) NSString *lemma;
#property (nonatomic,retain) NSString *value;
#end
#implementation Verbs
#synthesize lemma,value;
#end
then i read in the json and create the mappings with below code
objectManager = [RKObjectManager objectManagerWithBaseURL:#"http://localhost:3000"];
objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:#"RKRelationshipMappingExample.sqlite"];
[RKObjectManager setSharedManager:objectManager];
RKObjectMappingProvider* provider = [[RKObjectMappingProvider new] autorelease];
RKObjectMapping* verbMapping = [RKObjectMapping mappingForClass:[Verbs class]];
[verbMapping mapKeyPath:#"lemma" toAttribute:#"lemma"];
[verbMapping mapKeyPath:#"value" toAttribute:#"value"];
[provider setMapping:verbMapping forKeyPath:#"magic_verbs"];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/api/users/1/magic_words" objectMapping:verbMapping delegate:self];
I have done the same for the adjs mapping. I also have a class called words that contains 2 nsarrays that is to contain the object mapping data. but im unsure how to implement this and link them up correctly.
#interface Words : NSObject {
NSArray *_verbs;
NSArray *_adjs;
}
#property (nonatomic, retain) NSArray *verbs,*adjs;
#end
any help and guidance on this is appreciated. I have looked at the example in catalog project and have been able to get that running but havent been able to master the concept to apply it to my own json files.
thanks
G
I suggest you to look at the new documentation of OM 2.0 that is in the Github page here
if you haven't noticed yet. It clearly lays out on how to map your JSON to an object.