I’ve never used an API in conjunction with web services before and I’m having trouble parsing the JSON data I’m receiving from Flickr’s API. The only thing I do know (from all the things I have read) is that it is easy and very simple. About as far as I can get is returning a string in the console. Using a dictionary returns null and or an error. What am I missing? I want to be able to pull out the id and owner so that I can get the photo url.
This returns data on my photo:
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#”%#”, json); //this returns data on my photo
This returns null(resultDict) and error 3840:
NSString *requestString = #”https://api.flickr.com/services/rest?&method=......etc;
NSURL *url = [NSURL URLWithString:requestString];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *task = [session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSMutableDictionary *resultdict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(#”%#”, resultDict); //returns null
If (error != nil) { NSLog(#”%#”, [error localizedDescription]); }
else { self.myDict = [[resultDict objectforKey:#”photos”] objectAtIndex:0];
NSLog(#”%#”, self.myDict); }
}];
[task resume];
To check if I have an array of dictionaries I did the following and it returned 0:
NSMutableArray *resultArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainer error:&error]; error:&error];
NSLog(#"%lu", (unsigned long)resultArray.count);
Are you sure that
[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
returns a Dictionary and not an Array of Dictionnaries?
EDIT :
Can you try to use this to request the API please?
I checked in my projects, my reponses seems to have the same syntax as yours.
Only the code I use is different.
(If you could give us the full URL you've to call, it would be easier for us ^^')
NSString *str=#"YOUR URL";
NSURL *url=[NSURL URLWithString:str];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse:&response error: &error];
NSMutableDictionary* resultList = [NSJSONSerialization JSONObjectWithData:returnData options:NSJSONReadingMutableContainers error:nil];
I copied it without the errors. I let you manage that ^^
NSMutableDictionary ***resultdict** = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(#”%#”, **resultDict**); //returns null
resultdict and resultDict are not the same variables. I bet you have an instance variable or a global variable somewhere named resultDict. Instance variables should start with an underscore, among other reasons because it avoids problems like this.
For question maker this answer won't be actual but maybe for them who will face with such problem.
I had the same problem when tried to get data from flickr.photos.getRecent method. I forgot to add into URL parametrs value nojsoncallback=1.
Without it you get response in JSONP.
Related
I am aware that there are many similar SO questions that have a similar title to mine. I have checked them out and am still running into this problem.
I am trying to access an API that returns a string that should/could be formatted as JSON.
To retrieve this string as convert the string to JSON I'm using (unsuccessfully) this code:
NSError *error;
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#",responseString);
NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSLog(#"%#",[json objectForKey:#"ID"]);
NSLog(#"Json: %#",json);
}];
[task resume];
The NSLog(#"Response:...) returns a string that when I enter it into this website: http://jsonviewer.stack.hu confirms that the string is valid JSON.
Both NSLog's that are supposed to return a JSON value come back null.
What iv'e tried:
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&error];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
I have also now tried:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSMutableDictionary *jsonObject;
NSError *err = nil;
#try
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
}
#catch (NSException *exception)
{
NSLog( #"Exception caught %#",exception);
}
NSDictionary *info = jsonObject;
NSLog(#"Json: %#",info);
}];
[task resume];
What am I doing wrong here? How can I get a NSDictionary (JSON) result.
Your main issue:
If you read the error parameter of +JSONObjectWithData:options:error:, it will tell you this:
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start
with array or object and option to allow fragments not set."
UserInfo={NSDebugDescription=JSON text did not start with array or
object and option to allow fragments not set.}
As stated, your answer looks like this: aStringKey = realAndValidJSONSurroundedByCurvyBrackets, which is not a valid JSON.
After discussion in chat, you have contact with the server side, and it should be their responsibility to give proper JSON. Until they fix it, in order to keep working, you can do:
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
str = [str stringByReplacingCharactersInRange:NSMakeRange(0, [#"aStringKey = " length] ) withString:#""];
NSError *jsonError = nil;
NSDictionary *jsonFinal = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&jsonError];
if (jsonError)
{
NSLog(#"Error: %#", jsonError);
}
But remember, that's a "quick hack/fix" and shouldn't be left in final version and remove as soon as possible.
You tried:
NSError *err = nil;
#try
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
}
#catch (NSException *exception)
{
NSLog( #"Exception caught %#",exception);
}
The #try{}#catch(NSException *exception) shouldn't work since +JSONObjectWithData:options:error: shouldn't throw a NSException in your case, so in theory, there is nothing to catch, and but it may still not work (since there is a NSError).
Of course, since data parameter should be non null, if it's nil, you'll get a NSException (which would log Exception caught data parameter is nil), but that's another issue and doesn't assure you that the parsing went wrong (because of invalid JSON like in our case) if there is no exception.
Try this:
NSMutableDictionary *jsonObject;
NSError *err = nil;
#try
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
}
#catch (NSException *exception)
{
NSLog( #"Exception caught %#",exception);
}
NSDictionary *info = jsonObject;
I'm getting JSON data from the web, parse it, then using it to diplay pins on a map.
Here is method one, which there is no problem with:
NSString *CLIENT_ID = #"SECRET_ID";
NSString *CLIENT_SECRET = #"CLIENT_SECRET";
NSString *SEARCH = [NSString stringWithFormat:#"https://api.foursquare.com/v2/venues/search?near=gjovik&query=cafe&client_id=%#&client_secret=%#&v=20140119", CLIENT_ID, CLIENT_SECRET];
NSURL *searchResults = [NSURL URLWithString:SEARCH];
NSData *jsonData = [NSData dataWithContentsOfURL:searchResults];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
self.venues = dataDictionary[#"response"][#"venues"];
[self loadAnnotationsAndCenter:YES];
[self loadAnnotationsAndCenter:YES]; retrieves the lat and lng from the JSON file and uses it to display pins on the map.
I decided to change my code uing NSURLSession. Here's what it looks like:
NSString *CLIENT_ID = #"SECRET_ID";
NSString *CLIENT_SECRET = #"CLIENT_SECRET";
NSString *SEARCH = [NSString stringWithFormat:#"https://api.foursquare.com/v2/venues/search?near=gjovik&query=cafe&client_id=%#&client_secret=%#&v=20140119", CLIENT_ID, CLIENT_SECRET];
NSURL *URL = [NSURL URLWithString:SEARCH];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
self.venues = dataDictionary[#"response"][#"venues"];
[self loadAnnotationsAndCenter:YES];
}];
[task resume]
NSURLSession is between 10 and 30 seconds slower than the first method. I don't understand why that is. The search string, in this case, returns 27 different locations, if that matters
Cheers.
You need to ensure that UIKit methods will be executed on the main thread.
The block of the completion handler will be executed on the "delegate queue" (see delegateQueue property of NSURLSession).
You can accomplish this in two ways: either setup the delegate queue which is the main queue ([NSOperationQueue mainQueue]) or use
dispatch_async(dispatch_get_main_queue(), ^{
[self loadAnnotationsAndCenter:YES];
});
in your completion block.
In Swift:
dispatch_async(dispatch_get_main_queue()) {
}
So I asked this question recently and I was able to make it work
objective c calling wcf rest service request
Here's my code
NSString *urlStringRequest = [NSString stringWithFormat:#"http://service.mydomain.com/UserAccountService.svc/UserLogin?id=%#&pword=%#", email, incPassword];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStringRequest]];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSError *error = nil;
NSURLResponse *response = nil;
NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
id jsonResult = [self parseJsonResult:result];
NSLog(#"Json Result Woop: %#", jsonResult);
the parseJsonResult is exactly the same as the one that was on the link. My problem is though, whenever I would show the jsonResult on the NSLog, it would show the json result which is:
Json Result Woop: {
Authenticated = 0;
Message = "Invalid Log In.";
}
But I'm not really sure how I can go into the variable and retrieve the key and it's value one by one.
I tried to do this:
NSError *jsonParseError = nil;
NSMutableArray *jsonArray = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:&jsonParseError];
if(!jsonArray)
NSLog(#"Parse error: %#", jsonParseError);
else
for(NSDictionary *item in jsonArray)
NSLog(#"%#", item);
using the result variable but it would just give me the keys.
Not really sure what to do.
JsonResult is already an NSDictionary as evidenced by NSLog showing it like this with curly braces. Just use
jsonResult[#"Message"]
to get to the value for the Message key.
I'm working on a method to centralize my URL connections for sending and receiving JSON data from a server. It works with POST, but not GET. I'm using a Google App Engine server and on my computer it'll handle the POST requests and return proper results (and log appropriately), but I get the following error when I try the request with a GET method:
Error Domain=kCFErrorDomainCFNetwork Code=303 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 303.)" UserInfo=0xd57e400 {NSErrorFailingURLKey=http://localhost:8080/api/login, NSErrorFailingURLStringKey=http://localhost:8080/api/login}
In addition, the GAE dev server shows a "broken pipe" error, indicating that the client closed the connection before the server was finished sending all data.
Here's the method:
/* Connects to a given URL and sends JSON data via HTTP request and returns the result of the request as a dict */
- (id) sendRequestToModule:(NSString*) module ofType:(NSString*) type function:(NSString*) func params:(NSDictionary*) params {
NSString *str_params = [NSDictionary dictionaryWithObjectsAndKeys:func, #"function", params, #"params", nil];
NSString *str_url = [NSString stringWithFormat:#"%#%#", lds_url, module];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:str_url]];
NSData *data = [[NSString stringWithFormat:#"action=%#", [str_params JSONString]] dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPMethod:type];
[request setHTTPBody:data];
[request setValue:[NSString stringWithFormat:#"%d", [data length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSError *error = nil;
NSURLResponse *response = nil;
NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"Error: %#", error);
NSLog(#"Result: %#", [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]);
return [result objectFromJSONData];
}
A sample call would be:
NSDictionary *response = [fetcher sendRequestToModule:#"login" ofType:#"GET" function:#"validate_email" params:dict];
Again, this works with a POST but not a GET. How can I fix this?
In my case i was not calling [request setHTTPMethod: #"POST" ]
I think the root cause is you have an invalid URL.
JSON encoding will include things like '{', '}', '[' and ']'. All of these need to be URL encoded before being added to a URL.
NSString *query = [NSString stringWithFormat:#"?action=%#", [str_params JSONString]];
query = [query stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", str_url, query]];
To directly answer your question:
According to CFNetwork Error Codes Reference the error is kCFErrorHTTPParseFailure. This means the client failed to correctly parse the HTTP response.
The reason why is that a GET doesn't include a body. Why would you want to submit JSON in a GET anyways?
If the target api returns data only you pass it in the url params.
If you want to send data and "get" a response use a post and examine the body on return.
Sample Post:
NSError *error;
NSString *urlString = [[NSString alloc] initWithFormat:#"http://%#:%#/XXXX/MVC Controller Method/%#",self.ServerName, self.Port, sessionId ];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding ]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
// hydrate the remote object
NSString *returnString = [rdc JSONRepresentation];
NSData *s10 = [returnString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:s10];
NSURLResponse *theResponse = [[NSURLResponse alloc] init];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&error];
NSString *message = [[NSString alloc] initWithFormat: #"nothing"];
if (error) {
message = [[NSString alloc] initWithFormat:#"Error: %#", error];
} else {
message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
NSLog(#"%#", message);
return message;
I am working on a method to communicate between my PHP api and a iOS application. This is the reason why i wrote a function wich will get an external XML feed(of my api) and parse it.
But in the process to do that, i found a problem. The next code i wrote won't work:
-(void)getXML:(NSURL *)url {
NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
NSURLResponse *resp = nil;
NSError *err = nil;
NSData *response = [NSURLConnection sendSynchronousRequest: theRequest returningResponse: &resp error: &err];
xmlDocument = [[GDataXMLDocument alloc]initWithData:response options:0 error:&error];
NSArray *data = [[xmlDocument rootElement]elementsForName:#"api"];
data_from_xml = [[NSMutableArray alloc]init];
for(GDataXMLElement *e in data)
{
[data_from_xml addObject:e];
}
NSLog(#"xmlDocument:%#]\n\nData:%#\n\nData_from_xml:%#\n\nURL:%#", xmlDocument,data,data_from_xml, url);
}
The log returns:
xmlDocument:GDataXMLDocument 0x5a1afb0
Data:(null)
Data_from_xml:(
)
URL:http://sr.site-project.nl/api/?t=store.search.keyword
So, it seems that GDataXMLDocument has the XML. But i can't load it with the elementsForName argument?.
Does someone see what the problem is?
The XML:
<?xml version="1.0"?>
<api><type>core.error</type><errorMessage>API store.search.keyword doesn't exists</errorMessage></api>
The api node is your root element:
NSArray *data = [[xmlDocument rootElement]elementsForName:#"api"];
Try:
NSError *error = nil;
NSArray *data = [xmlDocument nodesForXPath:#"//api" error:&error];
I personally prefer the nodesForXPath method for retrieving elements.