I am using RKObjectMapping to map the response JSON to my predefined model but I am facing some issues when I try to map array of dictionaries.
JSON Response :
{
total: 274,
per_page: 10,
current_page: 1,
last_page: 28,
prev_page_url: null,
from: 1,
to: 10,
data: - [
- {
_id: "someid",
legacy_id: someid,
username: "awesome",
legacy_account_id: 12326,
brand_name: "Awesome",
last_review_ids: - [
],
category_profession_ids: - [
],
score_award: - {
},
score_award_one: - {
},
photos: - [
- {
_id: "someid"
},
- {
_id: "someanotherid"
}
}
],
enabled_at_utc: "2015-08-04 04:06:26",
published_at_utc: "2016-11-10 11:05:55",
last_activity_at_utc: "2016-04-10 16:17:06",
last_booked_at_utc: "2016-11-15 08:51:31",
last_reviewed_at_utc: "2016-11-08 10:50:13",
created_at_utc: "2015-08-04 04:03:59",
updated_at_utc: "2016-11-16 04:31:10"
},
+ {... },
+ {... },
+ {... },
+ {... },
+ {... },
+ {... },
+ {... },
+ {... },
+ {... }
],
client_version: "1"
}
And for mapping above response I have added below mapping methods
+ (RKObjectMapping *)professionalMapping{
// setup object mappings
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[ProfessionalModel class]];
[mapping addAttributeMappingsFromArray:baseModelMapping];
[mapping addAttributeMappingsFromArray:#[#"username", #"first_name", #"last_name", #"brand_name", #"profile_url", #"image", #"account_id", #"service_rules", #"lead_time_cancellation", #"faqs", #"work_experience", #"education", #"accolades", #"phone_no", #"contact_no", #"contact_no_hide", #"email", #"email_hide", #"website", #"website_hide", #"facebook", #"twitter", #"instagram", #"service_location", #"service_location_hide", #"housecall", #"housecall_charges", #"housecall_charges_display", #"published_at", #"enabled_at", #"created_at", #"updated_at", #"is_registered", #"id_no", #"id_full_name", #"id_front_image", #"id_back_image", #"id_address1", #"id_address2", #"id_postal_code", #"id_city", #"id_state", #"id_country", #"service_count", #"review_count", #"booking_count", #"share_url", #"distance", #"distance_unit", #"distance_display", #"neighbourhood", #"photo_count", #"review_photo_count", #"work_photo_count", #"location_photo_count", #"location_type", #"is_bookmarked", #"bookmark_id",#"user_id",#"is_followed",#"menus_display", #"payout_outstanding_display", #"payout_complete_display", #"payout_total_display", #"payout_outstanding"]];
[mapping addAttributeMappingsFromArray:#[#"rating_overall", #"rating_accuracy", #"rating_communication", #"rating_skill", #"rating_value", #"rating_attitude", #"rating_cleanliness", #"rating_environment"]]; // Rating attr
[mapping addAttributeMappingsFromArray:#[#"rating_overall_stats", #"rating_communication_stats", #"rating_attitude_stats", #"rating_cleanliness_stats", #"rating_environment_stats", #"rating_service_stats"]];
[mapping addAttributeMappingsFromArray:#[#"bank_id", #"bank_account_holder_name", #"bank_account_no", #"bank_branch_code", #"bank_code"]]; // Payout Method attr
[mapping addAttributeMappingsFromArray:#[#"transaction_fee_count_display",#"transaction_fee_count",#"currency_code",#"is_top_artist",#"top_artist_category_ids"]];
[mapping addAttributeMappingsFromDictionary:#{#"price_new_customer_fee_display": #"price_transacton_fee_display"}];
[mapping addAttributeMappingsFromDictionary:#{#"description": #"description2"}];
[mapping addRelationshipMappingWithSourceKeyPath:#"user" mapping:[self accountMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"country" mapping:[self countryMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"currency" mapping:[self currencyMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"timezone" mapping:[self timezoneMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"service_address" mapping:[self addressMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"service_addresses" mapping:[self addressMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"service_address_display" mapping:[self addressMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"id_home_addr" mapping:[self addressMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"menu" mapping:[self professionMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"photos" mapping:[self photoMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"reviews" mapping:[self bookingCustomerReviewMapping]];
[mapping addRelationshipMappingWithSourceKeyPath:#"last_reviews" mapping:[self bookingCustomerReviewMapping]];//because account (?)
return mapping;
}
+ (RKObjectMapping *)professionalTotalMapping{
RKObjectMapping* professionalMapping = [self professionalMapping];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[ProfessionalTotalMapping class]];
[mapping addAttributeMappingsFromArray:#[#"total"]];
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"data" toKeyPath:#"professional" withMapping:professionalMapping]];
return mapping;
}
But all I get is nil initialised ProfessionalTotalMapping and the code can't even map a single key.
Please let me know whats wrong in my mapping code.
Any help would be much appreciated.
Thanks
Found the solution by not creating 2 separate models.
So now I am parsing the whole response including all those required fields and creating a single model object which has all the data that I need.
Thanks
I have JSON of following kind
{
"Client": {
"FirstName": "String",
"LastName": "String",
"Email": "String",
"Password": "String",
"PhoneNumber": "String",
"Token": "String"
},
"ErrorMessage": "String",
"FriendlyMessage": "String"
}
I know that while mapping if I specify a key path then RestKit matches fields from object specified, in this case Client, to the code I am using for that is as follows
[responseMapping addAttributeMappingsFromDictionary:#{
#"FirstName": #"FirstName",
#"LastName": #"LastName",
#"Email": #"Email",
#"PhoneNumber": #"PhoneNumber",
#"Token": #"Token",
#"Password": #"Password",
#"ErrorMessage": #"ErrorMessage"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodPOST
pathPattern:nil
keyPath:#"Client"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
But I have two additional fields to map which aren't under client object. How do I map ErrorMessage and FriendlyMessage fields ?
Generally you wouldn't want to map them into your client object, you would want to map them into some other object. You would usually use a different object and response mapping, where the response mapping uses a different key path.
If for some reason you did want them in the same object then you could look at using the #metadata / #parent to get to the parent (https://github.com/RestKit/RestKit/pull/1435).
The API need to pass an array in an url query parameter, how to acheive this in iOS?
I only know how to pass a single vaue, like the API below : ?title=Design Milk&id=feed/http://feeds.feedburner.com/design-milk
API sample:
"title": "Design Milk",
"id": "feed/http://feeds.feedburner.com/design-milk",
"categories": [
{
"id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/design",
"label": "design"
},
{
"id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/weekly",
"label": "weekly"
},
{
"id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/global.must",
"label": "must reads"
}
]
Create a collection and then use NSJSONSerialization to create JSON data representation. Use the resulting data as the POST data.
NSDictionary *parameters = #{
#"title": #"Design Milk",
#"id": #"feed/http://feeds.feedburner.com/design-milk",
#"categories": #[
#{
#"id": #"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/design",
#"label": #"design"
},
#{
#"id": #"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/weekly",
#"label": #"weekly"
},
#{
#"id": #"user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/global.must",
#"label": #"must reads"
}
]
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictData options:0 error:&error];
I have the following JSON structure which I want to map with RestKit in CoreData:
[
{
"id": 1,
"type": "folder",
"name": "Folder 1",
"children": [
{
"id": 1,
"type": "document",
"name": "Document 1"
},
{
"id": 2,
"type": "folder",
"name": "Folder 2",
"children": [
{
"id" : 2,
"type": "document",
"name": "Document 2"
}
]
}
]
}
]
It's a typical filesystem structure where a folder can contain other folders or documents.
I have two CoreData entities: Folder and Document.
The Folder entity has the following relationships:
Here is my Mapping Code:
RKEntityMapping* foldersMapping = [RKEntityMapping mappingForEntityForName:#"Folder" inManagedObjectStore:managedObjectStore];
foldersMapping.identificationAttributes = #[ #"objectId" ];
[foldersMapping addAttributeMappingsFromDictionary:#{#"id" : #"objectId",
#"name" : #"name"}];
RKEntityMapping* documentsMapping = [RKEntityMapping mappingForEntityForName:#"Document" inManagedObjectStore:managedObjectStore];
documentsMapping.identificationAttributes = #[ #"objectId" ];
[documentsMapping addAttributeMappingsFromDictionary:#{#"id" : #"objectId",
#"name" : #"name"}];
In my dynamic mapping I use the type of the object to decide if it is a document or a folder.
RKDynamicMapping* dynamicMapping = [RKDynamicMapping new];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
if ([[representation valueForKey:#"type"] isEqualToString:#"Folder"]) {
return foldersMapping;
} else if ([[representation valueForKey:#"type"] isEqualToString:#"Document"]) {
return documentsMapping;
}
return nil;
}];
Now I added the dynamic mapping as relationship mapping to the folder mapping for the property children two times.
[foldersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"children" toKeyPath:#"children" withMapping:dynamicMapping]];
[foldersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"children" toKeyPath:#"documents" withMapping:dynamicMapping]];
And here is my Problem: All this works, but I got the following Warnings:
RestKitNesterFolderTestProject[9969:3a03] W
restkit.object_mapping:RKMappingOperation.m:554 WARNING: Failed
mapping nested object: The operation couldn’t be completed. (Cocoa
error 1550.)
I think the Problem is that I doubled the mapping for the relationship children, so RestKit tries to map each child two times, once as Folder and once as Document.
Any help is appreciated.
I am using RestKit to map the following JSON to core data.
This is my model:
this is my methods:
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([User class]) inManagedObjectStore:managedObjectStore];
userMapping.identificationAttributes = #[#"id"];
[userMapping addAttributeMappingsFromDictionary:#{
#"birthday":#"birthday",
#"email":#"email",
#"facebook_token":#"facebook_token",
#"first_name":#"first_name",
#"gender":#"gender",
#"interested_in":#"interested_in",
#"last_name":#"last_name",
#"password":#"password",
#"m8_status":#"m8_status",
#"status":#"status",
#"languages": #"languages",
#"hometown":#"hometown",
#"location":#"location",
#"avatar_url":#"avatar_url",
#"mood":#"mood"
}];
RKEntityMapping *findUserMapping = [RKEntityMapping mappingForEntityForName:NSStringFromClass([FindUser class]) inManagedObjectStore:managedObjectStore];
findUserMapping.identificationAttributes = #[#"id"];
[findUserMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"users" toKeyPath:#"users" withMapping:userMapping]];
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:findUserMapping
pathPattern:#"/v1/users"
keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]
]];
This is getting objects from example JSON:
{
"users": [
{
"avatar_url": null,
"birthday": "04/11/1984",
"email": "antonina.duminskaya#wildermancruickshank.co.uk", "first_name": "Antonina",
"gender": "female",
"hometown": null,
"id": "5264ee384d61632c4b2f0000",
"interested_in": [
"male",
"female" ],
"languages": [], "last_name": "Duminskaya", "location": null, "m8_status": null,
"mood": null,
"status": null
} ]
}
here:
[objectManager getObjectsAtPath:#"/v1/users" parameters:#{#"session_id":session.id} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
FindUser *findUsers = [[self.fetchedFindUsersResultsController fetchedObjects] lastObject];
NSLog(#"%#", findUsers.users);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
I have message in log:
Relationship 'users' fault on managed object (0x8e964a0) <FindUser: 0x8e964a0> (entity: FindUser; id: 0x8da0790 <x-coredata://083BB731-FDD7-4A35-A174-724F50862A02/FindUser/p1> ; data: {
id = nil;
users = "<relationship fault: 0x8deb260 'users'>";
})
And In mappingResults I have all results about json
Core Data is representing the relationship initially as fault saving memory by not loading all the object graph at once.
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreData/Articles/cdFaultingUniquing.html
Also, do not use the reserved word 'id' as the Restkit identification attribute for your managed objects. Try using findUserId and userId and update your mappings accordingly.