JSON
{
"entity": {
"id": 1,
"name": "name"
},
"settings": {
"key": "value"
}
}
Entity Model
Attributes: id, name
Relationship: settings(one to one)
Settings Model
Attributes: key
Relationship: entity(reverse relationship, one to one)
EntityMapping
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
mapping.persistentStore = [RKManagedObjectStore defaultStore].persistentStoreCoordinator.persistentStores.firstObject;
mapping.identificationAttributes = #[#"id"];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"name" : #"name"
}];
Had settings been inside entity in json response, I would add this
[mapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"settings"
toKeyPath:#"settings" withMapping:[Settings map]]
];
But my response id different than this, so how should I map correctly?
Both entity and settings are in a container dictionary, so you can change your response descriptor to not drill down so far and then use key paths to drill into the entity part and have direct access to the settings part:
[mapping addAttributeMappingsFromDictionary:#{
#"entity.id" : #"id",
#"entity.name" : #"name"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"settings" toKeyPath:#"settings" withMapping:[Settings map]]];
Related
I am creating a response descriptor for json to core data mapping in RestkitManager. The parent object is "level" and it has an array of "sublevel" objects.
RKDynamicMapping *levelMapping = [Level map];
RKResponseDescriptor* levelRd = [RKResponseDescriptor responseDescriptorWithMapping:levelMapping method:RKRequestMethodGET pathPattern:#"entity/:entityId" keyPath:#"summary.levels" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager addResponseDescriptor:levelRd];
In Level class
+ (RKEntityMapping *)mapping {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"name" : #"name",
#"state" : #"state"
}];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"sublevel" toKeyPath:#"sublevelList" withMapping:[Sublevel map]]];
return mapping;
}
In Sublevel
+ (RKEntityMapping *)map {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([self class]) inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"staticNode.obj.name" : #"name"
}];
return mapping;
}
When I try to fetch sublevel on object level, I get it in random order. Sometimes 2nd sublevel get printed first. Is there any way to maintain the order?
As I understand, when mapping is done, I don't have any control over what is getting persisted in the database. Hence, I am not able to assign any order number myself. Apart from that, I have explored metadata.routing.parameters but for this I need to pass parameters in the API call itself - which is not desirable.
Any pointers on how to maintain the order would be helpful.
I have the following JSON:
"Menus":{
"Id" : "3",
"Name" : "Cheese Burger",
"Items": []
}
I map the response to Core Data like so:
+ (RKEntityMapping *)menuMapping {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Menu" inManagedObjectStore:[[CoreDataManager sharedInstance] objectStore]];
mapping.assignsNilForMissingRelationships = YES;
mapping.assignsDefaultValueForMissingAttributes = YES;
[mapping addAttributeMappingsFromDictionary:#{
#"Id": #"remoteID",
#"Name": #"name",
}];
mapping.identificationAttributes = #[ #"remoteID" ];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"Items"
toKeyPath:#"products"
withMapping:[MappingProvider productMapping]]];
return mapping;
}
+ (RKEntityMapping *)productMapping {
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"Product" inManagedObjectStore:[[CoreDataManager sharedInstance] objectStore]];
mapping.assignsNilForMissingRelationships = YES;
[mapping addAttributeMappingsFromDictionary:#{
#"Id": #"remoteID",
#"Name": #"name",
#"Description" : #"info",
#"Price": #"price"
}];
mapping.identificationAttributes = #[ #"remoteID" ];
return mapping;
}
How would I manage to validate if the Items array is empty or not, and create the Menu object in Core Data based on if the Items (the products) contains values? I have tried using the assignsNilForMissingRelationships but it does not seem to work in this case.
Use KVC validation to analyse the incoming value and (modify or) reject it.
How cant I set a static value while mapping entities?
I have a JSON response like this:
"friends": [
{
"id": 123,
"name": "Friend",
},
]
"featured": [
{
"id": 456,
"name": "Some Featured user",
},
]
My mapping and descriptors look like this:
RKMapping *friendsMapping = [ProjectMappingProvider userMapping];
RKMapping *featuredMapping = [ProjectMappingProvider featuredUserMapping];
RKResponseDescriptor *friendsResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:friendsMapping
method:RKRequestMethodGET
pathPattern:#"/api/users"
keyPath:#"friends"
statusCodes:statusCodeSet];
RKResponseDescriptor *featuredResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:friendsMapping
method:RKRequestMethodGET
pathPattern:#"/api/users"
keyPath:#"featured"
statusCodes:statusCodeSet];
RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[
friendsResponseDescriptor,
featuredResponseDescriptor]];
... some code emited for readabilty ...
Now mu friendsResponseDescriptor and featuredResponseDescriptors look almost identical, but I would like to set additional CoreData parameter accordingly. Objects mapped through friendsDescriptor should have section = 0 and objects mapped through featured descriptor should have section = 10.
So, can I do something like this?
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"User"
inManagedObjectStore:[[DataModel sharedDataModel] objectStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"name": #"name" }];
mapping.identificationAttributes = #[ #"userId" ];
// How can I do somethning like this?
[mapping setValue:#0 forKey:#"section"];
And the featured mapping:
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:#"User"
inManagedObjectStore:[[DataModel sharedDataModel] objectStore]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"name": #"name" }];
mapping.identificationAttributes = #[ #"userId" ];
// How can I do somethning like this?
[mapping setValue:#10 forKey:#"section"];
Note that I don't have any other indicator whetever user is a friend or featured in the user JSON itself. The only way I can distinguish the type of user (friend,featured) is in which list in JSON response the user is set.
I am later using the section property in the table view controller to have sections.
If you are using different entities, set default values on them. If you aren't using different entities, consider changing so that you are (they could be sub-entities of a common parent).
You can't insert data into the mapping. The mapping only describes what RestKit should process. To edit the values you would need to get involved in the mapping process yourself and implement some delegate methods to inject additional data.
I'm doing a GET request with RESTKit, and I need sone help mapping the JSON response.
Here is the response that I need to map:
{"limit_hit":false,"providers":
[{"id":876553,
"name":"Cooper, Bradley N, DDS",
"specialty_groups":["Other Provider"],
"tags":[],
"has_comments":false,
"number_of_comments":0,
"locations":
[{"address":"1234 Rifle Range Road, El Cerrito, CA, 94530",
"providers_at_address_count":1,
"client_product_count":0,
"non_client_product_count":2,
"address_lines":["1234 Rifle Range Road, El Cerrito, CA, 94530"],
"address_id":234578,
"specialty_groups":
[{"specialty_group":"Other Provider"}],
"provider_types":
[{"provider_type":"Other Provider"}]},
{"address":"7501 Mission Rd, Shawnee Mission, KS, 66208",
"providers_at_address_count":2,
"client_product_count":0,
"non_client_product_count":2,
"address_lines":["7654 Main S, El Cerrito, CA, 94530"],
"address_id":654432,
"specialty_groups":
[{"specialty_group":"Other Provider"}],
"provider_types":
[{"provider_type":"Other Provider"}]
}]
}]
}
I want to be able to map both addresses, but I don't know how. All I'm able to do currently is map the id, name, has_comments, and number_of_comments (I'm using the keypath of "providers").
Here is my current mapping provider:
+ (RKMapping *)searchMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[ProviderSearch class]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"doctorID",
#"name": #"name",
}];
return mapping;
}
What exactly am I doing wrong, and how do I fix it?
Create another method to return the mapping for locations and then associate that mapping to this original one. Like this:
// ProviderLocation.m
+ (RKObjectMapping *)objectMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[ProviderLocation class]];
[mapping addAttributeMappingsFromDictionary:#{
#"address": #"address",
...
}];
return mapping;
}
Relationship:
+ (RKObjectMapping *)searchMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[ProviderSearch class]];
[mapping addAttributeMappingsFromDictionary:#{
#"id": #"doctorID",
#"name": #"name",
}];
RKObjectMapping *locationsMapping = [ProviderLocation objectMapping];
[mapping addPropertyMapping:
[RKRelationshipMapping relationshipMappingFromKeyPath:#"locations" toKeyPath:#"locations" withMapping:locationsMapping]];
return mapping;
}
Just remember to create a NSArray property in ProviderLocation.h named locations.
i've never used RKObjectMapping before but the "locations" you have there are an array of dictionary objects. so you would need an
NSArray loc = [myJson objectForKey:#"locations"];
for(NSDictionary *dict in loc){
//here each dict obj will have your "address", "providers_at_address_count" and etc... so if you want to access any of them you can call...
NSString *addr = [dict objectForKey:#"address"];
}
now somehow convert that to what you are doing with RXObjectMapping and you are golden =P
Fairly new to RestKit I run into a problem where I download public gist data.
I created an RKObjectManager and two RKResponseDescriptor objects with a corresponding RKEntityMapping objects like so.
RKEntityMapping *userEntityMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:managedObjectStore];
userEntityMapping.identificationAttributes = #[#"userID"];
[userEntityMapping addAttributeMappingsFromDictionary:#{
#"id" : #"userID",
#"avatar_url" : #"avatarURL",
#"gravatar_id" : #"gravatarID",
#"url" : #"jsonURL",
#"login" : #"login"}];
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userEntityMapping pathPattern:#"/gists/public" keyPath:#"user" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:userResponseDescriptor];
// Gist Entity
RKEntityMapping *gistEntityMapping = [RKEntityMapping mappingForEntityForName:#"Gist" inManagedObjectStore:managedObjectStore];
gistEntityMapping.identificationAttributes = #[ #"gistID" ];
[gistEntityMapping addRelationshipMappingWithSourceKeyPath:#"user" mapping:userEntityMapping];
[gistEntityMapping addAttributeMappingsFromDictionary:#{
#"id": #"gistID",
#"url": #"jsonURL",
#"description": #"descriptionText",
#"public": #"public",
#"created_at": #"createdAt"}];
RKResponseDescriptor *gistResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:gistEntityMapping pathPattern:#"/gists/public" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:gistResponseDescriptor];
The problem arises from the problem that sometimes the user field in the json is null and I don't know how to cope with that.
In fact I got an exception here
static NSDictionary *RKEntityIdentificationAttributesForEntityMappingWithRepresentation(RKEntityMapping *entityMapping, NSDictionary *representation)
and found the array processed here contains the null values, which lead to the exception.
- (NSArray *)mapRepresentations:(id)representations atKeyPath:(NSString *)keyPath usingMapping:(RKMapping *)mapping
Is there a way to handle those situations?
EDIT:
The exception is an
NSCAssert([representation isKindOfClass:[NSDictionary class]], #"Expected a dictionary representation");
in RKManagedObjectMappingOperationDataSource
static NSDictionary *RKEntityIdentificationAttributesForEntityMappingWithRepresentation(RKEntityMapping *entityMapping, NSDictionary *representation)
As far as the JSON is concerned, it looks sometimes like this.
{
"url": "https://api.github.com/gists/5661319",
"forks_url": "https://api.github.com/gists/5661319/forks",
"commits_url": "https://api.github.com/gists/5661319/commits",
"id": "5661319",
"git_pull_url": "https://gist.github.com/5661319.git",
"git_push_url": "https://gist.github.com/5661319.git",
"html_url": "https://gist.github.com/5661319",
"files": {
"header.php": {
"filename": "header.php",
"type": "application/httpd-php",
"language": "PHP",
"raw_url": "https://gist.github.com/raw/5661319/12598e6d41015248235058cda806722b30f81dca/header.php",
"size": 173
}
},
"public": true,
"created_at": "2013-05-28T08:29:35Z",
"updated_at": "2013-05-28T08:29:36Z",
"description": "",
"comments": 0,
"user": null,
"comments_url": "https://api.github.com/gists/5661319/comments"
},