RestKit EntityMapping with nested array - ios

I have a problem with entity mapping part of my JSON response.
I've tried to make a seperate entity for Ingredients in my Core Data model and set the mappingForEntityName to #"Ingredients", but to no avail. In my Dish Core Data entity model I have declared the #"ingredientValue", #"ingredientName", and so on, but when looking in my .sqlite file, it doesn't populate the fields for ingredients.
RestKit doesn't throw any errors back at me. So I'm suspecting it has something to do with the ingredients being stored in an array?
As you can see I've declared 2 RKEntityMapping instances (I don't know if this is the correct way of doing it?). But my ingredients are not returned.
The JSON looks like this:
{ dishes: [
{
id: 34,
name: "Pavlova med citroncreme og jordbær",
header: "En laekker marengs kage til enhver lejlighed",
howto: "lots of text....",
image: "/img_path/img.jpg",
created_at: "2014-04-05T00:25:22.378Z",
ingredients: [
{
ingredient_id: 27,
ingredient_amount: 3,
ingredient_unit: "stk",
ingredient_name: "æggehvider"
},
{
ingredient_id: 28,
ingredient_amount: 2,
ingredient_unit: "dl",
ingredient_name: "sukker"
}
]
}
]}
Dish.h:
#interface Dish : NSManagedObject
#property (nonatomic, copy) NSString *dishName;
#property (nonatomic, copy) NSString *dishHeader;
#property (nonatomic, copy) NSString *dishImage;
#property (nonatomic, copy) NSString *dishIngredients;
#property (nonatomic) NSInteger dishID;
#property (nonatomic, copy) NSData *dishMainText;
#property (nonatomic) NSDate *createdAt;
//Ingredients
#property (nonatomic, copy) NSString *ingredientName;
#property (nonatomic, copy) NSString *ingredientUnit;
#property (nonatomic) NSInteger ingredientValue;
#property (nonatomic) NSInteger ingredientID;
#end
Dish.m:
RKEntityMapping *dishMapping = [RKEntityMapping mappingForEntityForName:#"Dish" inManagedObjectStore:managedObjectStore];
[dishMapping addAttributeMappingsFromDictionary:#{
#"id": #"dishID",
#"name": #"dishName",
#"header": #"dishHeader",
#"howto": #"dishMainText",
#"image": #"dishImage",
#"created_at": #"createdAt"}];
dishMapping.identificationAttributes = #[ #"dishID" ];
RKEntityMapping *ingredientMapping = [RKEntityMapping mappingForEntityForName:#"Dish" inManagedObjectStore:managedObjectStore];
[ingredientMapping addAttributeMappingsFromDictionary:#{
#"ingredient_amount": #"ingredientValue",
#"ingredient_unit": #"ingredientUnit",
#"ingredient_name": #"ingredientName"}];
[dishMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"dishes" toKeyPath:#"dishes" withMapping:ingredientMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dishMapping
method:RKRequestMethodGET
pathPattern:#"/dishes.json"
keyPath:#"dishes"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
EDIT Added part of the log output for RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace)
with mapping <RKEntityMapping:0x8f69750 objectClass=NSManagedObject propertyMappings=(
"<RKAttributeMapping: 0x8f6c150 name => dishName>",
"<RKAttributeMapping: 0x8f6c190 id => dishID>",
"<RKAttributeMapping: 0x8f6c480 header => dishHeader>",
"<RKAttributeMapping: 0x8f6c4a0 howto => dishMainText>",
"<RKAttributeMapping: 0x8f6c520 image => dishImage>",
"<RKAttributeMapping: 0x8f6c5c0 created_at => createdAt>",
"<RKRelationshipMapping: 0x8f6d8e0 dishes => dishes>"

I don't see dishes defined in your Dish entity. It should be a to-many relationship to Ingredient.
ingredientMapping should use the Ingredient entity.
Where you call dishMapping addPropertyMapping:..., you should be using ...FromKeyPath:#"ingredients" ... to match the key name in the JSON coming in.

Related

Rest Kit core data over writing master data during GET store of entity calls

I have an entity called Currency and Another entity called Order which has a relationship of one to one with Currency. The currency is mapped with an identification attribute code like this:
l_mapping.identificationAttributes = #[#"code"];
Now when Order call is successful the currency relationship in the Order with same code say USD over writes the one in the Currency table. How can i avoid this? is there a way to just reference it and not create/overwrite the data in Currency table. Any help is much appreciated.
Here is a cut down version of the JSON and model classes.
#class Order;
#interface Currency : NSManagedObject
#property (nonatomic,retain) NSNumber * version;
#property (nonatomic, retain) NSString * code;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) Order *billCurrencyInv;
#end
RKEntityMapping* Mapping = [RKEntityMapping mappingForEntityForName:#"Currency" inManagedObjectStore:managedObjectStore];
Mapping.identificationAttributes = #[ #"code" ];
[Mapping addAttributeMappingsFromDictionary:#{
#"code": #"code",
#"version": #"version",
#"name": #"name"
}];
{
 "List": [
    {
     "code": "GBP",
     "version": 1,
     "name": "Pound"
    },
    {
     "code": "USD",
     "version": 1,
     "name": "US Dollars"
    }
 ],
 "totalCount": 2
}
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Currency;
#interface Order : NSManagedObject
#property (nonatomic,retain) NSNumber * version;
#property (nonatomic, retain) NSString * createdBy;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) Currency * billCurrency;
#end
RKEntityMapping* orderMapping = [RKEntityMapping mappingForEntityForName:#"Order" inManagedObjectStore:managedObjectStore];
orderMapping.identificationAttributes = #[ #"type" ];
[orderMapping addAttributeMappingsFromDictionary:#{
#"type": #"type",
#"createdBy": #"createdBy",
#"version": #"version"
}];
[orderMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"billCurrency"
toKeyPath:#"billCurrency"
withMapping:Mapping]];
{
 "List": [
    {
     "type": "Order”,
     "createdBy": "Me",
     "version": 1,
     "billCurrency": {
       "code": "GBP",
       "name": "British Pound"
     },
],
}
What happens is that the currency with GBP name as "Pound" is overwritten as "British - Pound". Which i dont want. I would like know if there is a way to let rest kit know that it has to reference the currency object of GBP and not create or overwrite it when making a core data object of order. I have tried will all types of relationships(order - currency) ie one-to-one, many-to-one. But to no use. if i set bill currency to transient it just does not have any data in Order which i guess is the right thing to happen. Thanks in advance.

RestKit 0.22 : how to map only firstObject of an array?

I am receiving more data than needed: I only want to map first object of an array of objects.
Using RestKit 0.22, how to map the following Json:
{ "name": "foobar",
"tags": ["tag1", "tag2", "tag3"] }
With the following model:
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *firstTag;
To map name, I have this code:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"MyObjectModel" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{#"name": #"name"}];
To map firstObject of tags to firstTag, I do not know how to proceed. Note: MyObjectModel is an NSManagedObject, so firstTag is #dynamic.
The tried and tested approach is to create a method on the managed object which takes the full array. That method extracts the first item and saves it to the actual NSString property. Then your mapping is as standard as all of the data types match.

storing a composite pattern (tree) in CoreData with Restkit

I have create an composite object (tree structure) in visual studio and stored in an sql server database with.
here are the screenshots:
now I want to create this model schema in core data and store the received data with RestKi.
here what I have designed with Xcode:
could someone help me to store this data with RestKit which I get in json ?
//Initialize managed object store
.
.
.
//Complete Core Data stack initialization
.
.
.
RKEntityMapping *menuMapping = [RKEntityMapping mappingForEntityForName:#"Menu" inManagedObjectStore:managedObjectStore];
menuMapping.identificationAttributes = #[#"menuComponentID"];
[menuMapping addAttributeMappingsFromDictionary:#{
#"ID":#"menuComponentID",
#"Name":#"name",
#"Description":#"descriptions",
#"Thumbnail":#"thumbnail",
#"Image": #"image",
}];
RKEntityMapping *menuItemMapping = [RKEntityMapping mappingForEntityForName:#"MenuItem" inManagedObjectStore:managedObjectStore];
[menuItemMapping addAttributeMappingsFromDictionary:#{
#"ID" : #"menuComponentID",
#"Description" : #"descriptions",
#"Name" : #"name",
#"Thumbnail":#"thumbnail",
#"Image":#"image",
#"Calorie":#"calorie",
#"Vegeterian":#"vegeterian",
}];
RKRelationshipMapping *menuRelationShip = [RKRelationshipMapping relationshipMappingFromKeyPath:#"MenuComponents" toKeyPath:#"children" withMapping:menuMapping];
[menuMapping addPropertyMapping:menuRelationShip];
RKResponseDescriptor *menuResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:menuMapping
method:RKRequestMethodGET
pathPattern:#"/api/menu/"
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:menuResponseDescriptor];
managed objects:
menuComponnet.h
#class MenuComponent;
#interface MenuComponent : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * descriptions;
#property (nonatomic, retain) NSString * image;
#property (nonatomic, retain) NSString * thumbnail;
#property (nonatomic, retain) NSString * menuComponentID;
#property (nonatomic, retain) MenuComponent *parent;
#end
////
menu.h
#class MenuComponent;
#interface Menu : MenuComponent
#property (nonatomic, retain) NSSet *children;
#end
#interface Menu (CoreDataGeneratedAccessors)
- (void)addChildrenObject:(MenuComponent *)value;
- (void)removeChildrenObject:(MenuComponent *)value;
- (void)addChildren:(NSSet *)values;
- (void)removeChildren:(NSSet *)values;
#end
menuItem.h
#interface MenuItem : MenuComponent
#property (nonatomic, retain) NSNumber * price;
#property (nonatomic, retain) NSNumber * calorie;
#property (nonatomic, retain) NSNumber * vegeterian;
#end
when I browse the created sqlite data base. I see there is only one created table name menuComponent which has all attributes of all menu and and menuItem. while nhibernate bas created for me three table as you see in the above picture.
and parent field for all of the records is null!!
how can I successfully store this data? please explain. I'm new to RestKit.
Default JSON I got:
[{"ID":"213028b8-9f59-4d7f-bae9-a2c301815a43","MenuComponents":[{"ID":"d1a861d3-d9e8-41a4-9577-a2c301815a44","MenuComponents":[{"ID":"fa6d172d-ab26-4782-8306-a2c301815a44","Vegetarian":false,"Calorie":0,"Name":"Double Chocolate Cake","Description":"With a melted heart of chocolate","Parent":{"ID":"d1a861d3-d9e8-41a4-9577-a2c301815a44"},"Version":"AAAAAAAAF64="},{"ID":"f9050f6b-8419-4457-9ebd-a2c301815a44","Vegetarian":false,"Calorie":0,"Name":"Tiramisu","Description":"Lady finger cookies are soaked in coffee liqueur and rum","Parent":{"ID":"d1a861d3-d9e8-41a4-9577-a2c301815a44"},"Version":"AAAAAAAAF60="}],"Name":"Dessets Menu","Description":"Desserts for serving after meal","Parent":{"ID":"213028b8-9f59-4d7f-bae9-a2c301815a43"},"Version":"AAAAAAAAF6o="},{"ID":"1f50565a-eeb2-4ab0-ba7b-a2c301815a44","MenuComponents":[{"ID":"0352c5a2-081a-496d-86aa-a2c301815a44","Vegetarian":true,"Calorie":0,"Name":"Grilled Chicken Salad","Description":"Sliced, grilled chicken tops a fresh salad mix, peas","Parent":{"ID":"1f50565a-eeb2-4ab0-ba7b-a2c301815a44"},"Version":"AAAAAAAAF6w="},{"ID":"4fad8aa0-285f-4419-b85e-a2c301815a44","Vegetarian":true,"Calorie":0,"Name":"Carolina Chicken Salad","Description":"Salad mix, fried chicken tenders, peas, cheddar cheese","Parent":{"ID":"1f50565a-eeb2-4ab0-ba7b-a2c301815a44"},"Version":"AAAAAAAAF6s="}],"Name":"Salad & Combinations","Description":"Salad & Combinations","Parent":{"ID":"213028b8-9f59-4d7f-bae9-a2c301815a43"},"Version":"AAAAAAAAF6k="}],"Name":"Lunch Menu","Description":"Lunch Super Menu","Version":"AAAAAAAAF6E="},{"ID":"fa6d172d-ab26-4782-8306-a2c301815a44"},{"ID":"0352c5a2-081a-496d-86aa-a2c301815a44"},{"ID":"d1a861d3-d9e8-41a4-9577-a2c301815a44"},{"ID":"f9050f6b-8419-4457-9ebd-a2c301815a44"},{"ID":"4fad8aa0-285f-4419-b85e-a2c301815a44"},{"ID":"1f50565a-eeb2-4ab0-ba7b-a2c301815a44"}]
you can view the json here:
http://jsonviewer.stack.hu/
UPDATE:
here is menuComponent RelationShip
RKRelationshipMapping *menuComponentRelationShip = [RKRelationshipMapping relationshipMappingFromKeyPath:#"MenuComponents" toKeyPath:#"children" withMapping:menuComponentMapping];
[menuComponentMapping addPropertyMapping:menuComponentRelationShip];
but I must map menuItem too because some property are exclusively for menuItem.
RKEntityMapping *menuItemMapping = [RKEntityMapping mappingForEntityForName:#"MenuItem" inManagedObjectStore:managedObjectStore];
[menuItemMapping addAttributeMappingsFromDictionary:#{
#"ID" : #"menuComponentID",
#"Description" : #"descriptions",
#"Name" : #"name",
#"Thumbnail":#"thumbnail",
#"Image":#"image",
#"Prcie":#"price",
#"Calorie":#"calorie",
#"Vegeterian":#"vegeterian",
}];
RKResponseDescriptor *menuItemresponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:menuItemMapping
method:RKRequestMethodGET
pathPattern:#"/api/menu/"
keyPath:nil
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:menuItemresponseDescriptor];
but after adding this responseDescriptor to mobjetManager only addes one super menu!!
How can I map menuItem and add them to database too?
Your issue doesn't seem to be related to RestKit at all, rather it is misunderstandings about Core Data.
Your children relationship should be on menu component and the inverse of the parent relationship. This allows Core Data to manage the relationships correctly (and as you expect).
You should only have 1 table in the SQLite DB file because you are using entity inheritance (it isn't a problem, just an implementation detail).
So just change the relationship in the model and you should be good to go.

RestKit: mapping to NSDictionary with values of specific type

I've got the following JSON coming from my webservice:
"GasPrices":{
"Ai92":{
"Price":30.1000,
"LastDate":"\/Date(1385337600000)\/",
"Votes":0
},
"Ai95":{
"Price":33.2000,
"LastDate":"\/Date(1385337600000)\/",
"Votes":0
}
I want to map it to NSDictionary, whose keys would be NSStrings and values would be of my custom class, say, PriceInfo.
What I got now with default setup is NSDictionary whose values are also NSDictionaries.
How can I achieve the desired mapping?
UPD. Here's my full setup for now.
#interface FillingStation : NSObject
#property (nonatomic, strong) NSNumber *uid;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSDictionary *fuelPrices;
#end
#interface PriceInfo : NSObject
#property (nonatomic, strong) NSNumber *price;
#property (nonatomic, strong) NSDate *lastDate;
#property (nonatomic, strong) NSNumber *votes;
#end
Configuring the mapping:
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[FillingStation class]];
[mapping addAttributeMappingsFromDictionary:#{
#"Id" : #"uid",
#"Title" : #"title",
#"GasPrices" : #"fuelPrices"
}];
This results in fuelPrices is NSDictionary with the structure:
NSString -> NSDictionary,
NSString -> NSDictionary,
...
And I want it to be:
NSString -> PriceInfo,
NSString -> PriceInfo,
...
And I don't want an intermediate dictionary in FillingStation which I can then manually map.
Okay, seems like I found the answer. Not exactly what I wanted, yet acceptable for me: https://github.com/RestKit/RestKit/wiki/Object-Mapping#handling-dynamic-nesting-attributes
I had to add another property to PriceInfo class and change fuelPrices from NSDictionary to NSSet (NSArray would also work, but I don't need ordering), so it became:
#interface FillingStation : NSObject
#property (nonatomic, strong) NSNumber *uid;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSSet *fuelPrices;
#end
#interface PriceInfo : NSObject
#property (nonatomic, strong) NSString *fuelType;
#property (nonatomic, strong) NSNumber *price;
#property (nonatomic, strong) NSDate *updateDate;
#property (nonatomic, assign) NSUInteger votes;
#end
and my mapping now looks like this:
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[FillingStation class]];
[mapping addAttributeMappingsFromDictionary:#{
#"Id" : #"uid",
#"Title" : #"title"
}];
RKObjectMapping *priceInfoMapping = [RKObjectMapping mappingForClass:[PriceInfo class]];
[priceInfoMapping setForceCollectionMapping:YES];
[priceInfoMapping addAttributeMappingFromKeyOfRepresentationToAttribute:#"fuelType"];
[priceInfoMapping addAttributeMappingsFromDictionary:#{
#"(fuelType).Price": #"price",
#"(fuelType).LastDate": #"updateDate",
#"(fuelType).Votes": #"votes"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"GasPrices"
toKeyPath:#"fuelPrices"
withMapping:priceInfoMapping]];
Jastor May be what you're looking for. Nested objects directly mapped to known classes.

How to add a connection to RKEntityMapping when it is contained in the same JSON

I'm trying to map a JSON response to instances of NSManagedObject, but i don't understand how to set the relationship/connection correctly.
My classes are:
#interface CloudObject : NSManagedObject
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) CloudFolder *parent;
#end
#interface CloudFolder : CloudObject
#property (nonatomic, retain) NSSet *children;
#end
#interface CloudFile : CloudObject
#property (nonatomic, retain) NSString * mimeType;
#end
JSON is something like this:
{
"name": "Folder1",
"is_dir": true,
"contents": [
{ "name": "Folder2", "is_dir": true }
{ "name": "File1", "is_dir": false, "mime_type": "audio/mpeg" }
]
}
I use RKDynamicMapping to map the response (root object):
RKEntityMapping *fileMapping = [RKEntityMapping mappingForEntityForName:#"CloudFile" inManagedObjectStore:managedObjectStore];
[fileMapping addAttributeMappingsFromDictionary:#{#"name": #"name", #"mime_type": #"mimeType"}];
RKEntityMapping *folderMapping = [RKEntityMapping mappingForEntityForName:#"CloudFolder" inManagedObjectStore:managedObjectStore];
[folderMapping addAttributeMappingsFromDictionary:#{#"name": #"name"}];
RKDynamicMapping *objectMapping = [[RKDynamicMapping alloc] init];
[objectMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:#"is_dir" expectedValue:[NSNumber numberWithBool:NO] objectMapping:fileMapping]];
[objectMapping addMatcher:[RKObjectMappingMatcher matcherWithKeyPath:#"is_dir" expectedValue:[NSNumber numberWithBool:YES] objectMapping:folderMapping]];
But how can I now setup the relationship/connection to #"contents" (which should be RKDynamicMapping too) and children/parent? (Folders contained in 'contents' do NOT have an 'contents'-attribute)
Take a look at the RestKit documentation for dynamic mappings : Dynamic Object Mapping.
According to the documentation, it looks like you should add this line, after creating your dynamic mapping objectMapping:
[folderMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contents" toKeyPath:#"children" withMapping:objectMapping]];
You specify the "contents" property as being mapped with your dynamic mapping. Also, you don't need to worry about the inner folders not having a "contents" field. If "contents" doesn't exist, RestKit should just ignore it gracefully, and your NSSet *children property will be automatically set to nil for that object.

Resources