I have this json object that I'm trying to map:
{
totalresults: 1,
results: [
{
sources: [
"The Blog",
"The website",
],
description: "Some description."",
subjects: [
"Education"
],
imageurl: "image.jpg",
authorid: 44303,
istail: false,
tailcount: 0,
displayname: "Name of me"
}
]
}
I've created a SearchPerPage object:
#interface RestSearchPerPage : NSObject
#property (strong, nonatomic) NSNumber *totalResults;
#property (strong, nonatomic) NSArray *results;
#end
I've created a SearchResults object:
#interface SearchResults : NSObject
#property (strong, nonatomic) NSArray *sources;
#property (strong, nonatomic) NSArray *subjects;
#property (strong, nonatomic) NSString *content;
#property (strong, nonatomic) NSString *imageUrl;
#property (strong, nonatomic) NSString *authorId;
#property (strong, nonatomic) NSString *displayName;
#property (strong, nonatomic) NSNumber *isTail;
#property (strong, nonatomic) NSNumber *tailCount;
#end
I'm mapping the objects just fine, but when I'm trying to map the NSArray *sources and NSArray *subjects I always get NULL.
This is my last mapping attempt on sources:
RKObjectMapping *searchResultsMapping = [RKObjectMapping mappingForClass:[SearchResults class]];
[searchResultsMapping addAttributeMappingsFromDictionary:#{ #"sources" : #"sources",
#"subjects" : #"subjects",
#"description" : #"content",
#"imageurl" : #"imageUrl",
#"authorid" : #"authorId",
#"displayname" : #"displayName",
#"istail" : #"isTail",
#"tailcount" : #"tailCount" }];
RKObjectMapping *searchPerPageMapping = [RKObjectMapping mappingForClass:[SearchPerPage class]];
[searchPerPageMapping addAttributeMappingsFromDictionary:#{ #"totalresults" : #"totalResults" }];
[searchPerPageMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"results" toKeyPath:#"results" withMapping:searchResultsMapping]];
[[RKObjectManager sharedManager] addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:searchPerPageMapping
method:self.method
pathPattern:kGetSearchPerPage
keyPath:nil
statusCodes:self.statusCode]];
Please help me because I'm lost, just don't know what to do next - have no answer to this problem. Thanks in advance.
So, the problem was that I didn't mapped the array of 'results' like that:
[searchPerPageMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"results" toKeyPath:#"results" withMapping:searchResultsMapping]];
I've edited my question and fixed it.
Are you making sure that you're initializing the arrays e.g.
NSArray *subjects = [NSArray array];
It's very common to forget this :)
Related
I am attempting to use RestKit to parse some JSON. The app is for tracking competitions, and has a concept of venues, games, and players. A game has a winner and loser (both of class Player) and a venue. The JSON returned by the web service looks like this:
[{"id":1,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME3","id":3,"picture_url":"NJ.png"},"venue":{"name":"Venue 1","id":2}},
{"id":2,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME1","id":1,"picture_url":"NC.png"},"venue":{"name":"Venue 1","id":2}},
{"id":3,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME3","id":3,"picture_url":"NJ.png"},"venue":{"name":"Venue 1","id":2}},
{"id":4,"winner":{"name":"NAME2","id":2,"picture_url":"CK.png"},"loser":{"name":"NAME1","id":1,"picture_url":"NC.png"},"venue":{"name":"Venue 1","id":2}}]
My object header files look like this:
# PTGame.h
#interface PTGame : NSObject
#property (nonatomic, assign) NSNumber *uid;
#property (nonatomic, assign) PTPlayer *winner;
#property (nonatomic, assign) PTPlayer *loser;
#property (nonatomic, assign) PTVenue *venue;
#end
# PTVenue.h
#interface PTVenue : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, assign) NSNumber *uid;
-(CLLocation *)location;
-(CLLocationDistance)distanceFromLocation:(CLLocation *)location;
#end
# PTPlayer.h
#interface PTPlayer : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *pictureUrl;
#property (nonatomic, assign) NSNumber *uid;
#end
Finally, my mapping setup looks like:
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[PTVenue class]];
[venueMapping addAttributeMappingsFromDictionary:#{#"name" : #"name",
#"id" : #"uid"}];
RKObjectMapping *playerMapping = [RKObjectMapping mappingForClass:[PTPlayer class]];
[playerMapping addAttributeMappingsFromDictionary:#{#"name" : #"name",
#"id" : #"uid",
#"picture_url": #"pictureUrl"}];
// Add Object Mapping for PTGame
RKObjectMapping *gameResponseMapping = [RKObjectMapping mappingForClass:[PTGame class]];
// Setup Game Object relationships
[gameResponseMapping addAttributeMappingsFromDictionary:#{#"id": #"uid"}];
[gameResponseMapping addRelationshipMappingWithSourceKeyPath:#"winner" mapping:playerMapping];
[gameResponseMapping addRelationshipMappingWithSourceKeyPath:#"loser" mapping:playerMapping];
[gameResponseMapping addRelationshipMappingWithSourceKeyPath:#"venue" mapping:venueMapping];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *gameListResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:gameResponseMapping
method:RKRequestMethodGET
pathPattern:#"/games.json"
keyPath:nil
statusCodes:statusCodes];
[objectManager addResponseDescriptor:gameListResponseDescriptor];
This all looks right to me. No errors are thrown when the result is returned, but later I get an exception when attempting to access a property of the game. A peek into the objects via XCode debugger shows this:
Something seems to be broken - why do those strings show as "winner" and "venue". Why is the uid null? The objects seem different every time I make the request, leading me to believe the memory is not be handled properly.
Any ideas?
Wain's comment led me down the path of looking outside of the mapping code. I looked in the RestKit Object Mapping docs and noticed all of the attributes were using the nonatomic and copy properties, rather than assign. Additionally, the object references (such as PTPlayer *winner were simply nonatomic. Changing my code to reflect this fixed the issue.
The resulting header files look like:
# PTPlayer.h
#interface PTPlayer : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *pictureUrl;
#property (nonatomic, copy) NSNumber *uid;
#end
# PTVenue.h
#interface PTVenue : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSNumber *uid;
#property (nonatomic, assign) CGFloat longitude;
#property (nonatomic, assign) CGFloat latitude;
-(CLLocation *)location;
-(CLLocationDistance)distanceFromLocation:(CLLocation *)location;
#end
# PTGame.h
#interface PTGame : NSObject
#property (nonatomic, copy) NSNumber *uid;
#property (nonatomic) PTPlayer *winner;
#property (nonatomic) PTPlayer *loser;
#property (nonatomic) PTVenue *venue;
#end
I have this issue, that i'm mapping all object properly, but the properties that are inside the root object, for some reason I cannot map.
This is the JSON:
{
"responseError": null,
"responseIsRegisteredServices": {
"registeredToServiceAntiVirus": true,
"registeredToServiceCloud": true,
"registeredToServiceMusic": true,
"registeredToServiceSong": true,
"registeredToServiceTv": true
},
"responseRegisteredServicesCloud": null,
"responseRegisteredServicesMusic": [
{
"ID": null,
"Name": null,
"AlbumPic": "110_110.png",
"ArtistName": "Rihanna"
},
{
"ID": null,
"Name": null,
"AlbumPic": "110_110.png",
"ArtistName": "Rihanna"
},
{
"ID": null,
"Name": null,
"AlbumPic": "110_110.png",
"ArtistName": "Steve Angello"
}
]
}
This is the RegisteredServices class:
#interface RegisteredServices : NSObject
#property (nonatomic) ResponseError *responseError;
#property (nonatomic) NSNumber *registeredToServiceAntiVirus;
#property (nonatomic) NSNumber *registeredToServiceCloud;
#property (nonatomic) NSNumber *registeredToServiceMusic;
#property (nonatomic) NSNumber *registeredToServiceSong;
#property (nonatomic) NSNumber *registeredToServiceTv;
#property (nonatomic) CloudServices *responseRegisteredServicesCloud;
#property (nonatomic) NSArray *responseRegisteredServicesMusix;
#end
This is the 'CloudServices' class:
#interface CloudServices : NSObject
#property (nonatomic) NSString *capacityType;
#property (nonatomic) NSNumber *currentCapacity;
#property (nonatomic) NSString *lastBackupDate;
#property (nonatomic) NSNumber *percentage;
#property (nonatomic) NSNumber *totalCapacity;
#end
This is the 'MusicServices' class:
#interface MusicServices : NSObject
#property (nonatomic) NSNumber *idNum;
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *albumPic;
#property (nonatomic) NSString *artistName;
#end
This is the mapping itself:
RKObjectMapping *musixMapping = [RKObjectMapping mappingForClass:[MusicServices class]];
[musicMapping addAttributeMappingsFromDictionary:#{ #"ID" : #"idNum",
#"Name" : #"name",
#"AlbumPic" : #"albumPic",
#"ArtistName" : #"artistName" }];
RKObjectMapping *cloudMapping = [RKObjectMapping mappingForClass:[CloudServices class]];
[cloudMapping addAttributeMappingsFromDictionary:#{#"capacityType": #"capacityType",
#"currentCapacity": #"currentCapacity",
#"lastBackupDate": #"lastBackupDate",
#"percentage": #"percentage",
#"totalCapacity": #"totalCapacity" }];
RKObjectMapping *registeredServicesMapping = [RKObjectMapping mappingForClass:[RegisteredServices class]];
[registeredServicesMapping addAttributeMappingsFromDictionary:#{ #"registeredToServiceAntiVirus" : #"registeredToServiceAntiVirus",
#"registeredToServiceCloud" : #"registeredToServiceCloud",
#"registeredToServiceMusic" : #"registeredToServiceMusic",
#"registeredToServiceSong" : #"registeredToServiceSong",
#"registeredToServiceTv" : #"registeredToServiceTv", }];
[registeredServicesMapping addRelationshipMappingWithSourceKeyPath:#"responseRegisteredServicesMusic" mapping:musixMapping];
[registeredServicesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"responseError"
toKeyPath:#"responseError"
withMapping:errorMapping]];
[registeredServicesMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"responseRegisteredServicesCloud"
toKeyPath:#"responseRegisteredServicesCloud"
withMapping:cloudMapping]];
[[RKObjectManager sharedManager] addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:registeredServicesMapping
method:RKRequestMethodGET
pathPattern:#"registeredServices"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
Can't figure out what seems to be the problem. When I add responseIsRegisteredServices into the keyPath: of the RKResponseDescriptor than the MusicServices array is null.
Thanks in advance.
So i've found the answer, in this line:
[registeredServicesMapping addAttributeMappingsFromDictionary:#{ #"registeredToServiceAntiVirus" : #"registeredToServiceAntiVirus",
#"registeredToServiceCloud" : #"registeredToServiceCloud",
#"registeredToServiceMusic" : #"registeredToServiceMusic",
#"registeredToServiceSong" : #"registeredToServiceSong",
#"registeredToServiceTv" : #"registeredToServiceTv", }];
I needed to change it from registeredToServiceAntiVirus to responseIsRegisteredServices .registeredToServiceAntiVirus and than it worked perfectly. This is how it looks:
[registeredServicesMapping addAttributeMappingsFromDictionary:#{ #"responseIsRegisteredServices.registeredToServiceAntiVirus" : #"registeredToServiceAntiVirus",
#"responseIsRegisteredServices.registeredToServiceCloud" : #"registeredToServiceCloud",
#"responseIsRegisteredServices.registeredToServiceMusic" : #"registeredToServiceMusic",
#"responseIsRegisteredServices.registeredToServiceSong" : #"registeredToServiceSong",
#"responseIsRegisteredServices.registeredToServiceTv" : #"registeredToServiceTv", }];
Enjoy.
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.
I have been struggling with this issue for some time now and I cant figure out why the JSON serialization is not working. What seems to be a simple task it taking way too long.
I have my model classes listed below. User object has NSArray of Education and Employment objects.
I am using RestKit and all of the relationships have been made and I can see that the dictionary of the parameters is being made the data is correct. In order to debug the issue further I am making JSON using the following code.
NSDictionary *parameters = [RKObjectParameterization parametersWithObject:self.user requestDescriptor:delegate.postUserRequestDescriptor error:&error];
// Serialize the object to JSON
BOOL isTurnableToJSON = [NSJSONSerialization
isValidJSONObject: parameters];
if(isTurnableToJSON){
NSData *JSON = [RKMIMETypeSerialization dataFromObject:parameters MIMEType:RKMIMETypeJSON error:&error];
}
isTurnableToJSON is always "NO". When I force creation of JSON I get the following error.
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Education)'
The same error is also thrown for employment if I remove the relationship of Education with user.
Here is my user, education, and location models.
#interface User : NSObject
#property (strong, nonatomic) NSString *first_name;
#property (strong, nonatomic) NSString *middle_name;
#property (strong, nonatomic) NSString *last_name;
#property (strong, nonatomic) NSString *profile_picture;
#property (strong, nonatomic) NSString *display_name;
#property (nonatomic, strong) NSArray *education;
#property (nonatomic, strong) NSArray *employment;
#property (nonatomic, strong) NSMutableArray *skills;
#property (strong, nonatomic) NSString *about_you;
#end
#interface Education : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *graduation_date;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *degree;
#property (nonatomic, strong) Location *location;
#property (nonatomic) NSNumber *current;
#end
#interface Location : NSObject
#property (nonatomic, strong) NSString *city;
#property (nonatomic, strong) NSString *state;
#property (nonatomic, strong) NSString *country;
#end
At this point I am at lose of any ideas and any help would be appreciated.
Update: RestKit Relationship mapping.
RKObjectMapping *locationMap = [RKObjectMapping mappingForClass:(Location.class)];
[locationMap addAttributeMappingsFromDictionary:#{#"city":#"city",#"state":#"state",#"country":#"country"}];
RKObjectMapping *educationMap = [RKObjectMapping mappingForClass:(Education.class)];
[educationMap addAttributeMappingsFromDictionary:# { #"name":#"name",#"graduation_date":#"graduation_date",#"major":#"major", #"degree":#"degree",#"current":#"current"}];
[educationMap addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"location" toKeyPath:#"location" withMapping:locationMap]];
RKObjectMapping *employmentMap =[RKObjectMapping mappingForClass:(Employment.class)];
[employmentMap addAttributeMappingsFromDictionary:#{#"company":#"company",#"occupation":#"occupation", #"start_date":#"start_date",#"end_date":#"end_date",#"current":#"current"}];
RKObjectMapping *userPutRequestMapping = [RKObjectMapping requestMapping];
[userPutRequestMapping addAttributeMappingsFromDictionary:putUserMap];
[userPutRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"education" toKeyPath:#"education" withMapping:[educationMap inverseMapping]]];
[userPutRequestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"employment" toKeyPath:#"employment" withMapping:[employmentMap inverseMapping]]];
USER PUT REQUEST
[objectManager addRequestDescriptor:[RKRequestDescriptor requestDescriptorWithMapping:userPutRequestMapping
objectClass:[User class]
rootKeyPath:nil
method:RKRequestMethodPUT]];
I've been trying to map the following object from the JSON response and from everything I see in the console output, there isn't any reason why the mapping isn't successful - I appreciate if anyone could have a check and see:
#interface RKElectionsModel : NSObject
#property (nonatomic, assign) bool isActive;
#property (nonatomic, strong) NSNumber *electionID;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *summary;
#property (nonatomic, strong) NSNumber *availableSeats;
#property (nonatomic, strong) NSNumber *candidatesCount;
#property (nonatomic, strong) NSNumber *withdrawnCount;
#property (nonatomic, strong) NSSet *candidates;
#end
/**
* Election Detail Mapping: Getting all election details, we have some extra information from
* the API call
*
*/
RKObjectMapping *electionDetailsMapping = [RKObjectMapping mappingForClass:[RKElectionsModel class]];
// Map JSON -> entities
[electionDetailsMapping addAttributeMappingsFromDictionary:#{
#"id": #"electionID",
#"title": #"title",
#"summary": #"summary",
#"active": #"isActive",
#"availableSeats": #"availableSeats",
#"candidatesCount": #"candidatesCount",
#"withdrawnCount": #"withdrawnCount"
}];
// Register our mappings with the provider
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:electionDetailsMapping pathPattern:#"/api/elections/:electionID/all" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Console output on pastebin and any examples of the JSON response you can visit here
Appreciate any help,
Lewis
Seems like your trying to map a NSString to NSNumber.
In your NSObject try to add the following validation and transform the string you receive from the server to a NSNumber when the mapping starts.
- (BOOL)validateAvailableSeats:(id *)ioValue error:(NSError **)outError {
// Force the value to be a NSNumber
*ioValue = [NSNumber numberWithDouble:[(NSString*)value doubleValue] ]
return YES;
}
In the RestKit Wiki you'll find more transformations for your values.