I can not seem to figure out how to map the following JSON, I am trying to map the hostedLargeUrl from the response. I am not sure what to do for this issue, and would like to apologize if my info is not detailed enough. Not too sure what type of details you would need.
Thank you in advance.
images: [
{
imageUrlsBySize: {
90: "http://lh4.ggpht.com/ZXiwjS55Zk7oBu6GWaVr0HAqIPKumXwBfGtzsCWEFdrJSOXiCcC-I3TpUwrXBnP_DPNuBm-ib-4-3aXbs4mfXA=s90-c",
360: "http://lh4.ggpht.com/ZXiwjS55Zk7oBu6GWaVr0HAqIPKumXwBfGtzsCWEFdrJSOXiCcC-I3TpUwrXBnP_DPNuBm-ib-4-3aXbs4mfXA=s360-c"
},
hostedLargeUrl: "http://i.yummly.com/Pasta-with-garlicky-broccoli-rabe-305651-270310.l.jpg",
hostedSmallUrl: "http://i.yummly.com/Pasta-with-garlicky-broccoli-rabe-305651-270310.s.jpg"
}
Here is my code:
+ (RKMapping *)recipeDetailMapping
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RecipeDetails class]];
[mapping addAttributeMappingsFromDictionary:#{
#"attribution.text" : #"attributionText",
#"images.hostedLargeUrl" : #"images"
}];
[mapping addAttributeMappingsFromArray:#[#"ingredientLines",
#"name",
#"totalTime",
]];
return mapping;
}
RecipeDetails
#property (nonatomic, copy) NSArray *ingredientLines;
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *totalTime;
#property (nonatomic, copy) NSString *attributionText;
#property (nonatomic, copy) NSString *images;
Last bit of code
- (void)loadRecipeDetails
{
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKMapping *mapping = [MappingProvder recipeDetailMapping];
NSString *resourcePath = [NSString stringWithFormat:#"/v1/api/recipe/%#", self.recipeInfo.recipeId];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodGET
pathPattern:resourcePath
keyPath:nil
statusCodes:statusCodeSet];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.yummly.com/v1/api/recipe/%#?_app_id=%#&_app_key=%#&requirePictures=true", self.recipeInfo.recipeId
,Yummly_APP_ID , Yummly_API_kEY ]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
self.recipeData = mappingResult.array;
[self updateUI];
[SVProgressHUD dismiss];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
NSLog(#"Response: %#", operation.HTTPRequestOperation.responseString);
[SVProgressHUD showErrorWithStatus:#"Request Failed"];
}];
[operation start];
}
The source data is in an array and you need to deal with that in some way.
Option 1.
Change the property to NSArray *images;
Option 2.
Add a custom method setImageArray: and implement it to extract the first image and store it. Then change the mapping to use a destination key of imageArray.
Other options really require a different object graph...
Related
I am learning Rest-kit. i am trying to parse this url https://api.coursera.org/api/catalog.v1/courses?fields=language,shortDescription
I have created one Courses class.
#interface Courses : NSObject
#property (nonatomic, strong) NSString *name;
#end
and in viewcontroller i have wrote below code
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureRestKit];
[self loadCourses];
}
- (void)configureRestKit
{
// https://api.coursera.org/api/catalog.v1/courses?fields=language,shortDescription
// initialize AFNetworking HTTPClient
NSURL *baseURL = [NSURL URLWithString:#"https://api.coursera.org"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
// initialize RestKit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
// setup object mappings
RKObjectMapping *courseMapping = [RKObjectMapping mappingForClass:[Courses class]];
[courseMapping addAttributeMappingsFromDictionary:#{
#"name":#"name"
}];
// register mappings with the provider using a response descriptor
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:courseMapping method:RKRequestMethodGET pathPattern:#"/api/catalog.v1/courses?fields=language,shortDescription" keyPath:#"elements" statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
}
- (void)loadCourses
{
[[RKObjectManager sharedManager] getObjectsAtPath:#"/api/catalog.v1/courses?fields=language,shortDescription" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#", mappingResult.array);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"What do you mean by 'there is no Courses?': %#", error);
}];
}
ERROR :_Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded."
I am not getting response in success block. Please correct me. Thanks in advance
Don't add your query parameters to the path pattern on the response descriptor, and set them as parameters on the request:
pathPattern:#"/api/catalog.v1/courses"
generally I would also say that /api/catalog.v1/ should be part of the base URL so you would have:
NSURL *baseURL = [NSURL URLWithString:#"https://api.coursera.org/api/catalog.v1/"];
...
... pathPattern:#"courses" ...
...
... getObjectsAtPath:#"courses" parameters:#{ #"fields" : #"language,shortDescription" } ...
I'm new using RestKit, but I can't understand at all how it works...
please, can somewhere explain me it??
My Json file is:
{
"colors":
{
"red":"#f00",
"green":"#0f0",
"blue":"#00f",
"cyan":"#0ff",
"magenta":"#f0f",
"yellow":"#ff0",
"black":"#000"
}
}
and the path where i'm hosting this file is: http://186.36.181.116/tesis/file.json
The code that I'm trying in my ViewDidLoad method is:
- (void)viewDidLoad
{
[super viewDidLoad];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[colores class]];
[mapping addAttributeMappingsFromArray:#[#"colors"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:#"/tesis/:coloresID" keyPath:#"colors" statusCodes:statusCodes];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://186.36.181.116/tesis/file.json"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
colores *colores = [result firstObject];
NSLog(#"Mapped the article: %#", colores);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
[operation start];
}
My Class "colores" is as follow:
#import <Foundation/Foundation.h>
#interface colores : NSObject{}
#property (weak, nonatomic) IBOutlet NSString *colores;
#end
Thank you so much in advance!!
you can find a detailed tutorial here and full source code github.
In order to properly map responses to JSON, we must do the following things:
*Create an instance of RKEntityMapping for each entity in our managed object model
*Add mappings between JSON response keys and object properties
*Add mappings between embedded JSON objects and relationships
*Create response descriptors with the mappings
*Optional: Create request descriptors with the mappings if you plan to PUT or POST
i'm having a web service that that collects the company names and phone numbers from the database. But sometimes a phone number is not provided. When i try to map the phone number to the object with restkit 'im getting a warning that value is nil sometimes. then i get the warning message:
Coercing NSNull value to nil in shouldSetValue:atKeyPath: -- should be fixed.
any suggestions how to map nil to NSNull?
this is my header file of my customerrealtion class (.h):
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#interface CustomerRelation : NSObject
#pragma private fields
#property (nonatomic, copy) NSString *companyName;
#property (nonatomic, copy) NSString *phoneNumber;
#end
implementation file of customer relation class (.m)
#import "CustomerRelation.h"
#implementation CustomerRelation
-(BOOL)ValidatecompanyName:(id*)ioValue error:(NSError **)outError {
NSLog(#"error");
}
-(BOOL)validatephoneNumber:(id *)ioValue error:(NSError **)outError {
if (*ioValue == nil) {
if (outError != NULL) {
}
return NO;
}
return YES;
}
#end
this is where i do the mapping:
- (void)viewDidAppear:(BOOL)animated{
RKObjectManager * client = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://127.0.0.1"]];
RKObjectMapping *articleMapping = [RKObjectMapping requestMapping];
[articleMapping addAttributeMappingsFromDictionary:#{#"Companyname": #"companyName",
#"Phonenumber": #"phoneNumber"}];
RKResponseDescriptor *rkresponsedesc = [RKResponseDescriptor responseDescriptorWithMapping:articleMapping method:RKRequestMethodGET pathPattern:nil keyPath:#"customers" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[client addResponseDescriptor:rkresponsedesc];
NSMutableURLRequest *request = [client requestWithObject:nil method:RKRequestMethodGET path:#"test.php" parameters:nil];
RKObjectRequestOperation *operation = [client objectRequestOperationWithRequest:request
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[self mappingSuccess:mappingResult];
} failure: ^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
[client enqueueObjectRequestOperation:operation];
MainViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"mainViewController"];
[self.navigationController pushViewController:viewController animated:YES];
}
- (void)mappingSuccess:(RKMappingResult*)mappingResult
{
NSLog(#"Success block: %#", mappingResult);
}
Seeing that error I guess you're using an old version of RestKit and that you should update.
That said you should be able to tie into this with KVC validation which RestKit supports. If you implement validateValue:forKey:error: on your destination model object you can verify and edit the value being set for any key.
i finally found why the validate methods were never called.
the error was in the line
RKObjectMapping *customerRelationMapping = [RKObjectMapping requestMapping];
what should get the class instead:
RKObjectMapping *customerRelationMapping = [RKObjectMapping mappingForClass:[Article class]];
I am trying to map results from a Foursquare (FS) call. When I use AFNetworking it returns the FS results as expected (JSON). When I try the call with Restkit, either with RKEntityMapping or RKObjectMapping, I get the following error.
RKMapperOperation.m:98 Adding mapping error: No mappable values found for any of the attributes or relationship mappings
Any suggestions? Am I missing something? I know I don't have the relationship mapping setup for some of the location info, but I can't seem to get it to map the top level stuff.
RESTKIT ===============
// --- VENUE MAPPING ------------------------------
RKEntityMapping *venueMapping = [RKEntityMapping mappingForEntityForName:#"Venue"
inManagedObjectStore:objectManager.managedObjectStore];
[venueMapping addAttributeMappingsFromArray:#[#"name"] ];
[venueMapping addAttributeMappingsFromDictionary:#{
#"id": #"venueID"
}];
[venueMapping setIdentificationAttributes:#[#"venueID"] ];
// Routes for Users
[self.foursquareManager.router.routeSet addRoute:[RKRoute routeWithClass:[Venue class]
pathPattern:#"v2/venues/search"
method:RKRequestMethodGET]];
// Register our mappings with the provider
RKResponseDescriptor *venueResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:venueMapping
pathPattern:#"v2/venues/search"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKResponseDescriptor *venueDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:venueMapping
pathPattern:#"v2/venues/search"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.foursquareManager addResponseDescriptor:venueResponseDescriptor];
[self.foursquareManager addResponseDescriptor:venueDescriptor];
[self.foursquareManager getObject:nil
path:#"v2/venues/search"
parameters:#{
#"client_id" : kFourSquareClientID,
#"client_secret" : kFourSquareClientSecret,
#"ll" : searchString,
#"v" : #"20130101"
}
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
DLog(#"Getting favorites return successful update tableview and CD ");
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
DLog(#"Getting all favorites failed: %#", [error localizedDescription]);
}];
AFNETWORKING =============
If I use AF to make the call it returns the JSON data perfectly.
NSString *searchString = [NSString stringWithFormat:#"%#, %#",[dictionary objectForKey:#"lat"], [dictionary objectForKey:#"lng"]];
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"https://api.foursquare.com"]];
NSDictionary *parameters = #{
#"client_id" : kFourSquareClientID,
#"client_secret" : kFourSquareClientSecret,
#"ll" : searchString,
#"v" : #"20130101"
};
NSMutableURLRequest *request = [client requestWithMethod:#"GET" path:#"/v2/venues/search" parameters:parameters];
DLog(#"");
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSError *error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:JSON //1
options:kNilOptions
error:&error];
NSLog(#"THIS IS A DICTIONARY (or array): %#", JSON);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"if requestFailed");
}];
[operation start];
MY Venue object ==========
#interface Venue : NSManagedObject
#property (nonatomic, retain) NSString * venueID;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * address;
#property (nonatomic, retain) NSNumber * verified;
#end
Found the issue. This will resolve it.
keyPath:#"response.venues"
The JSON that FourSquare returns has many attributes and sub arrays of info. The keyPath is the path you need to walk to get to the "venues" of the JSON data. If you need to get to the just the "location" info returned you would use the following keyPath (walk the hierarchy).
keyPath:#"response.venues.location"
For 2 days now, I've been trying to find out why I'm getting the error using iOS 6.1.3 with Xcode 4.6.2 and RestKit 0.20.0:
"...this class is not key value coding-compliant for the key Text."
The strange part is that I can receive (GET) the JSON object fine. The error happens when I create my sample SignalMessage object and then try to PUT it back to the server.
The JSON is as follows:
{"Text":"New Message","HasMessage":"true"}
The SignalMessage object looks like this:
#import <Foundation/Foundation.h>
#interface SignalMessage : NSObject {
}
#property (nonatomic, copy) NSString *signalText;
#property (nonatomic, retain) NSNumber *isHasMessage;
#end
And the implementation like this:
#import "SignalMessage.h"
#implementation SignalMessage
#synthesize isHasMessage, signalText;
#end
My correctly working getMessage function looks like this:
- (IBAction)getMessage:(id)sender;
{
NSLog(#"%#", #"Getting message... ");
NSURL *url = [NSURL URLWithString:#"http://ec2-54-243-148-145.compute-1.amazonaws.com/TabletPractice/api/signal?clientIdentifier=2"];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:url];
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[SignalMessage class]];
[responseMapping addAttributeMappingsFromDictionary:#{#"Text":#"signalText", #"HasMessage": #"isHasMessage"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[manager addResponseDescriptor:responseDescriptor];
[manager getObject:nil path:#"" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
NSArray *theresults = [result array];
for (SignalMessage *item in theresults) {
self.txtMessage.text = item.signalText;
[self hideControls];
}
} failure:^(RKObjectRequestOperation * operation, NSError * error)
{
NSLog (#"Server WS call failure: operation: %# \n\nerror: %#", operation, error);
}];
}
And here is the sendClicked message that gives me grieve:
- (IBAction)btnSendClicked:(id)sender;
{
if ([txtMessage.text length] < 1)
return;
NSURL *url = [NSURL URLWithString:#"http://ec2-54-243-148-145.compute-1.amazonaws.com/TabletPractice/api/signal?clientIdentifier=2"];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:url];
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[requestMapping addAttributeMappingsFromDictionary:#{#"Text":#"signalText", #"HasMessage": #"isHasMessage"}];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping
objectClass:[SignalMessage class]
rootKeyPath:#""];
[manager addRequestDescriptor:requestDescriptor];
SignalMessage *newMessage = [[SignalMessage alloc] init];
newMessage.signalText = #"Test Message";
BOOL isMsg = TRUE;
NSNumber *boolAsNumber = [NSNumber numberWithBool:isMsg];
newMessage.isHasMessage = boolAsNumber;
[manager putObject:newMessage path:#"" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSLog(#"We object mapped the response with the following result: %#", result);
} failure:^(RKObjectRequestOperation * operation, NSError * error)
{
NSLog (#"Server WS call failure: operation: %# \n\nerror: %#", operation, error);
}];
[self hideControls];
}
At this point, I'm at a loss.
Please add a inverse mapping to your RKRequestDescriptor in your btnSendClicked method like below:
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor
requestDescriptorWithMapping:[requestMapping inverseMapping]
objectClass:[SignalMessage class]
rootKeyPath:#""];