I am using AFJSONRequestOperation to request a remote API:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//Remove the SVProgressHUD view
[SVProgressHUD dismiss];
//Check for the value returned from the server
NSData *jsonData = [JSON dataUsingEncoding:NSUTF8StringEncoding];//This line cause crash
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:nil];
loginDic=[[NSDictionary alloc]init];
loginDic=[arr objectAtIndex:0];
NSLog(#"%#",loginDic);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#", [error.userInfo objectForKey:#"NSLocalizedDescription"]);
}];
[operation start];
[SVProgressHUD showWithStatus:#"Loading"];
However, the app crashes and I am getting this error:
[__NSCFDictionary dataUsingEncoding:]: unrecognized selector sent to instance
Here is an NSLog for the JSON object returned:
Result = (
{
operation = 5;
result = 1;
}
);
Am I missing something, because I think that I am not parsing correctly the JSON object. Please correct me.
It looks like AFJSONRequestOperation is deserializing JSON to a dictionary for you, and then you're trying to do it again. JSON is an NSDictionary but you're calling an NSString method.
Remove all of this code:
NSData *jsonData = [JSON dataUsingEncoding:NSUTF8StringEncoding];//This line cause crash
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:nil];
loginDic=[[NSDictionary alloc]init];
loginDic=[arr objectAtIndex:0];
And replace it with:
loginDic = [[JSON objectForKey:#"Result"] lastObject];
(That'll work safely without checking array bounds, but assumes that there's only one element in the array.)
The object you get in the success block is already parsed by AFJSONRequestOperation.
In your case you get a NSDictionary object.
You can check the class of the object using the isKindofClass-method:
if ([JSON isKindOfClass:[NSDictionary class]]) {
NSDictionary* dict = (NSDictionary*)JSON;
...
}
Related
I am trying to redo some code to use AFNetworking. I have this method below:
-(NSArray *)GetTableDataOfPhase:(NSString *)phase
{
NSString *phaseRequestString = [NSString stringWithFormat:#"%#?jobNo=%#",kIP,phase];
NSURL *JSONURL = [NSURL URLWithString:phaseRequestString];
NSURLResponse* response = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
if(data == nil)
return nil;
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
return tableArray;
}
and right now I am trying to alter it so it still returns an array, I have tried doing this:
-(NSArray *)GetTableDataOfPhase:(NSString *)phase
{
NSString *phaseRequestString = [NSString stringWithFormat:#"%#?jobNo=%#",kIP,phase];
NSURL *JSONURL = [NSURL URLWithString:phaseRequestString];
NSURLResponse* response = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData* data = [NSURLConnection sendSynchronousRequest:responseObject returningResponse:&response error:nil];
if(data == nil)
return nil;
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
return tableArray;
}
but I got this error:
/Users/jamessuske/Documents/My Programs/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:168:46: Incompatible block pointer types sending 'void *(^)(AFHTTPRequestOperation *, id)' to parameter of type 'void (^)(AFHTTPRequestOperation *, id)'
and this warning:
/Users/jamessuske/Documents/My Programs/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:170:97: Sending 'NSURLResponse *const *' to parameter of type 'NSURLResponse **' discards qualifiers
This is how I am calling it:
- (void)GetRequest
{
//refresh table view
[dataSource.editedCellHolder removeAllObjects];
[dataSource.cellHolder removeAllObjects];
[dataSource.cellHolderDisplay removeAllObjects];
NSArray *tableData = [dataSource.areaData GetTableDataOfPhase:[NSString stringWithFormat:#"%#%#",areaPickerSelectionString,unitPickerSelectionString]];
if(tableData == nil)
[self CustomAlert:#"Data was not recieved from the server, please check internet/VPN settings, Or contact Software Vendor for assistance."];
[dataSource PopulateTableData:tableData];
[indicatorView stopAnimating];
[indicatorView removeFromSuperview];
[loadingView removeFromSuperview];
loadingView = nil;
indicatorView =nil;
[NSTimer scheduledTimerWithTimeInterval:0.2f target:self selector:#selector(DisplayTable) userInfo:nil repeats:NO];
}
A couple of things:
Using AFNetworking, you should entirely lose the NSURLConnection request.
Likewise, the default responseSerializer does the JSON parsing for you, so you can lose the NSJSONSerialization parsing. AFNetworking does all of that for you.
Likewise, don't build URL parameters manually, but rather again let AFNetworking do that for you. By default, AFNetworking uses a requestSerializer that will build the request for you.
Your old method ran synchronously, which is generally a bad idea. Instead, you should use asynchronous patterns (e.g. a completionHandler block).
So, pulling all of this together, it probably looks like:
- (void)getTableDataOfPhase:(NSString *)phase completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"jobNo" : phase};
[manager GET:kIP parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
completionHandler(responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionHandler(nil, error);
}];
}
And you'd call it like so:
[self getTableDataOfPhase:#"..." completionHandler:^(NSArray *resultsObject, NSError *error) {
if (resultsObject) {
// use NSArray here
} else {
NSLog(#"error = %#", error);
}
}];
// but don't try to use the `resultsObject` array here!
I have have some trouble in understanding what is needed to fetch a JSON file with mantle.h from a URL.
Can someone give me an example of how it works?
For example:
-I have a URL www.example.com with a JSONFile as follows:
{
"name": "michael"
}
How could I fetch it?
I use this process for fetching JSON:
NSURL *s = url;//Put your desird url here
NSURLRequest *requestURL = [NSURLRequest requestWithURL:s cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.00];
NSHTTPURLResponse *response;
NSError *error = [[NSError alloc]init];
NSData *apiData = [NSURLConnection sendSynchronousRequest:requestURL returningResponse:&response error:&error];
dictionaryData = [NSJSONSerialization JSONObjectWithData:apiData options:kNilOptions error:&error];
Now the dictionaryData contains your JSON. You can fetch it by:
NSString *name = [dictionaryData valueForKey:#"name"];
And make sure you are making async request. For this put the code within this block:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//Put the code here
});
Hope this helps.. :)
Call it with following method
[super getRequestDataWithURL:urlString
andRequestName:sometext];
You will get response in the following method if successful
- (void)successWithRequest:(AFHTTPRequestOperation *)operation withRespose:(id)responseObject withRequestName:(NSString *)requestName {
NSString *response = operation.responseString;
id jsonObject = [response objectFromJSONString];
if(![super checkforServerRequestFailureErrorMessage:jsonObject]) {
[self.leaderboardProxyDelegate leaderboardListSuccessful:jsonObject];
}
}
You will get dictionary in jsonObject
I currentley have a jSON file which is created a by a script which can be seen here: http://ddelay.co.uk/bus/output.json
I currently have managed to setup the following to grab the JSON which works, i just cannot figure how to grab the data to assign it :/
-(void)makeStopRequests{
NSURL *url = [NSURL URLWithString:#"http://ddelay.co.uk/bus/output.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking Asynchronous Task
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id responseObject)
{
NSLog(#"JSON RESULT %#", responseObject);
self.stopArray = [responseObject objectForKey:#"stop_name"];
[self.tableView reloadData];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id responseObject)
{
NSLog(#"Request Failed: %#, %#", error, error.userInfo);
}];
[operation start];
}
I hope somebody can guide me through how I would select the data,
Thank you.
Damien
I would recommend just using something as simple as NSJSONSerialization, which is built into iOS after iOS 5 (may have been earlier, but not too sure).
Just get the data from the request and then parse that into a dictionary with the following code
NSDictionary *dict= [NSJSONSerialization JSONObjectWithData:webData options:NSJSONReadingMutableLeaves error:nil];
get the data using the standard built in networking structure as well
NSURLRequest *someRequest = [[NSURLRequest alloc] initWithURL:someURL cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:10.0];
NSURLConnection *someConnection= [[NSURLConnection alloc] initWithRequest:someRequest delegate:self];
And then do whatever appropriate delegation and data processing. Read the documentation to get more details
You are using AFNetworking and it provides you the response object. That is already being cast to Objective c object.
You can simply assign it to NSArray or NSDictionary like this
NSArray *array = (NSArray*)responseObject;//if your json returns an array
NSDictionay *dict (NSDictionary*)responseObject;//if json returns dictionary.
This url returns JSON in this format;
[ {
"service":"22",
"provider":"First in Yorkshire",
"dest":"Nether Edge to Woodhouse",
"dest URL":"/web/public_service_stops.asp?service=22&operatorid=31&systemid=30&goingto=Woodhouse"
},
{
"service":"22",
"provider":"First in Yorkshire",
"dest":"Barnsley to Rotherham",
"dest URL":"/web/public_service_stops.asp?service=22&operatorid=31&systemid=30&goingto=Rotherham"
}
]
Which actually is an array of dictionaries so to access data you have to loop like this.
NSArray *jsonResponse = (NSArray*)responseObject;
for (NSDictionary *dic in jsonResponse){
NSString *service = [dic valueForKey:#"service"];
NSString *provider = [dic valueForKey:#"provider"];
//Same for others
//It will better you create calss with these properties and then add that object to an Array, and on reloading data in table get that object from array in cellForRowAtIndexPath and use requried property to populate your data.
//Or if you just want to use stop name add stop name to array and use it.
NSString *stopName =[dic valueForKey:#"stop_name"];
//[dataArray addObject:stopName]; in case you want to use only stop name
}
Here's minimal code:
self.stopArray = responseObject;
for (NSDictionary *stopDict in self.stopArray) {
NSString *service = [stopDict objectForKey:#"service"];
NSString *provider = [stopDict objectForKey:#"provider"];
NSString *dest = [stopDict objectForKey:#"dest"];
NSString *destURL = [stopDict objectForKey:#"dest URL"];
// do something with this data
}
For anything more complex than this, you'll probably want to make your own data class, so you don't have to parse through dictionaries and arrays in your table view delegate methods.
I am doing an authentication by using AFNetworking like below
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// Parsing will be here
{
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"ERROR :jason is %#",JSON);
}];
[client enqueueHTTPRequestOperation:operation];
Below is a JSON which received from server
{
"first_name" = A;
"last_name" = B;
}
Question :
How can a parse this JSON in ios. I am stuck because the return from server does not have any tag at all. If its format was
{
"user": {
"first_name": "A",
"last_name": "B",
}
}
I could parse by doing the following
NSArray *userList = [[NSArray alloc] init];
userList = [JSON objectForKey:#"results"];
Any ideas?
"Tag" is not in the terminology of JSON. The complex structure of JSON is negotiated between the sender and receiver (or simply dictated by the sender) and need not follow any particular outline, so long as it parses correctly.
The first quasi-JSON string you quoted would (if it were valid JSON) presumably identify the first and last name of an individual, and you would presumably know that it was a "user" identity, and what user it identified, from the context.
In general, you must approach a JSON string as an onion, peeling one layer at a time. In the case of your first string there is only one layer, an "Object" that maps to an NSDictionary. So, having received the object (and, if necessary, verified that it is indeed an NSDictionary using isKindOfClass, you would cast the id value to an NSDictionary and proceed to use objectForKey or some such to access the values within.
You should utilize the new JSONSerialization class available in iOS5 which makes JSON easy to consume. Below is a quick example grabbing your json and parsing it in the fetchedData method. Resources listed below
//CALL dataWithContentsOfURL ONCE DATA HAS BEEN RECEIVED YOU CAN PARSE THE JSON:
NSError *error = nil;
NSData* data = [NSData dataWithContentsOfURL:kURL options:NSDataReadingUncached error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
} else {
NSLog(#"Data has loaded successfully.");
}
//MORE CODE HERE?
- (void)fetchedData:(NSData *)responseData {
_yourObjectArray = [[NSMutableArray alloc] init];
//parse out the json data
NSError* error;
NSArray* json = [NSJSONSerialization JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
for(int i=0; i<[json count]; i++){
YourObject *yObject = [[YourObject alloc]init];
NSDictionary* rawData = [json objectAtIndex:i]; //2
yObject.FirstName = [rawData objectForKey:#"first_name"];
yObject.LastName = [rawData objectForKey:#"last_name"];
[_yourObjectArray addObject:yObject];
}
}
SOURCES:
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html
http://www.raywenderlich.com/5492/working-with-json-in-ios-5
I created a subclass of AFHTTPClient and am trying to send some JSON parameters to a server.
However the server is responding with a Expected content type
{(
"text/json",
"application/json",
"text/javascript"
)}, got application/xml
According to AFNetworking FAQ
If you're using AFHTTPClient, set the parameterEncoding property to AFJSONParameterEncoding. Any method on that HTTP client with a parameters argument will now encode the passed object into a JSON string and set the HTTP body and Content-Type header appropriately.
I've done that here but the server appears not to recognize the content-headers. Does anyone know of a potential solution?
Here is the method:
- (void)getCompanyDataWithString:(NSString*)companySearchQuery
finish:(LBMarkitAPIRequestCompletionBlock)finishBlock
{
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setParameterEncoding:AFJSONParameterEncoding];
NSDictionary *params = [NSDictionary dictionaryWithObject:
companySearchQuery forKey:#"input"];
NSMutableURLRequest *searchQueryRequest = [self requestWithMethod:#"GET"
path:kMarkitCompanyURL parameters:params];
AFJSONRequestOperation *searchRequestOperation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:searchQueryRequest
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json)
{
NSLog(#"Response: %#", response);
NSLog(#"JSON: %#",json);
NSMutableArray *results = [NSMutableArray array];
NSError *anError = [[NSError alloc] init];
if ([json objectForKey:#"Message"])
{
NSString *message = [json objectForKey:#"Message"];
anError = [[NSError alloc] initWithDomain:message
code:100
userInfo:nil];
}
// Need some error handling code here
for (id item in json)
{
NSString *aName = [item objectForKey:#"Name"];
NSString *aSymbol = [item objectForKey:#"Symbol"];
NSString *anExchange = [item objectForKey:#"Exchange"];
LBCompany *aCompany = [[LBCompany alloc] initWithName:aName
Symbol:aSymbol Exchange:anExchange];
[results addObject:aCompany];
}
// Need to run the passed in block after JSON
// Request Operation succeeds
finishBlock(results,anError);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response,
NSError *error, id JSON)
{
NSLog(#"request failed: %#",[error localizedDescription]);
NSLog(#"Response: %#",response);
NSLog(#"JSON: %#",JSON);
}];
[searchRequestOperation start];
NSLog(#"JSON operation started");
}
The issue was with URL formatting. I didn't notice an API implementation detail that made sending Query Parameters necessary and also specifying JSON output in the URI.
There were no issues with regard to AFNetworking.