Convert NSData from NSURLSession to JSON - objective-c

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;

Related

Parsing Json to object giving "null" Value

Unable to parse this json data to object. Same code i tried with other URL, working correct. Please Suggest where i am doing wrong?
-(void)callAPI{
NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https:s.json"]];
NSError *error=nil;
id response=[NSJSONSerialization JSONObjectWithData:data options:
NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves error:&error];
if (error) {
NSLog(#"%#",[error localizedDescription]);
} else {
NSLog(#"%#",response);}}
Output The data couldn’t be read because it isn’t in the correct format.
I got the very perfect solution for your question which works fine now.Please check the below answer
- (void)callAPI
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:#"GET"];
[request setURL:[NSURL URLWithString:#"https://dl.dropboxusercontent.com/s/2iodh4vg0eortkl/facts.json"]];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
^(NSData * data,
NSURLResponse * response,
NSError * error) {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"jsonString is: %#", jsonString);
NSData *dataCon = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id jsonVal = [NSJSONSerialization JSONObjectWithData:dataCon options:0 error:nil];
if([jsonVal isKindOfClass:[NSDictionary class]]) {
NSLog(#"The response starts with NSDictionary");
NSArray *arrJsonVal = [jsonVal objectForKey:#"rows"];
NSMutableArray *arrTitle = [[NSMutableArray alloc]init];
NSMutableArray *arrDesc = [[NSMutableArray alloc]init];
NSMutableArray *arrImage = [[NSMutableArray alloc]init];
for(NSDictionary *dict in arrJsonVal) {
NSString *strTitle = [dict objectForKey:#"title"];
NSString *strDesc = [dict objectForKey:#"description"];
NSString *strImage = [dict objectForKey:#"imageHref"];
[arrTitle addObject:strTitle];
[arrDesc addObject:strDesc];
[arrImage addObject:strImage];
}
NSLog(#"arrTitle is - %#",arrTitle);
NSLog(#"arrDesc is - %#",arrDesc);
NSLog(#"arrImage is - %#",arrImage);
}else {
NSLog(#"The response starts with NSArray");
}
}] resume];
}
The Printed results are
After that
Then Array results are
Finally the results are

Trying to parse JSON data from Flickr. Cocoa error 3840

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.

AFJSONRequestSerializer POST JSON Array

I am trying to post to an API that is expecting a JSON array as input, for example:
[
{
"token": "bd3",
"display_order": 0
}
]
I am unable to get AFJSONRequestSerializer to produce this, my code to create the params :
NSDictionary *campParams = #{#"token" : #"bd3", #"display_order" : #"0" };
NSArray *arr = [NSArray arrayWithObject:campParams];
NSDictionary *final = [NSDictionary dictionaryWithObject:arr forKey:#""];
Which creates the JSON as:
{"":[{"token":"bd55","display_order":"0"}]}
How can I modify my code to get the proper JSON output? I am creating a second dictionary as the AFHTTPRequestOperationManager accepts parameters as a dictionary, not an array.
[manager PUT:url parameters:final success:^(AFHTTPRequestOperation *operation, id responseObject) {
//done
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
Don't add the last dictionary operation, the questions shows that a JSON array is expected..
NSArray * jsonArray = #[ #{ #"token" : #"bd3", #"display_order" : #"0" } ];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonArray options:0 error:&error];
NSLog(#"jsonData as String: %#", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
Output:
jsonData as String: [{"token":"bd3","display_order":"0"}]
There is a possibility that the last argument should be a number, not a string:
NSArray * jsonArray = #[ #{ #"token" : #"bd3", #"display_order" : #0 } ];
producing:
jsonData as String: [{"token":"bd3","display_order":0}]
which better matches your initial JSON example.
Here is an example but not using AFNetworking because I am not conversant in it:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPBody:jsonData];
[request setHTTPMethod:#"POST"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
NSLog(#"Error: %#", connectionError);
}
else {
NSLog(#"data as String: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
}];
Note: By all means use AFNetworking, it is great! I just know the older OSX/iOS methods by heart and I did not have a test project that already had AFNetworking installed.
My solution was to simply change the request serializer, I was not aware that it was not using JSON by default, so I changed it and now it works.

NSURL directory files

I have for example : www.example.com/ url.
Inside that URL there are few directories for example www.example.com/dir1/ and inside that there are images when clicked I get www.example.com/dir1/image1.jpg.
My problem is that I need to get all files inside www.example.com/dir1/ , so all images that are inside that directory on web. Based on that names I can get final url (like www.example.com/dir1/image1.jpg) but I need to get all the names of images and dunno how.
Thanks.
If you are seeing the apache directory listing you can parse that html and get all your .jpg files.
http://www.raywenderlich.com/14172/how-to-parse-html-on-ios is a tutorial on how to parse HTML
If this is on your server, you need some mechanism to retrieve the names of the files.
For example, if PHP, this is a script that returns a JSON response of all of the JPG/PNG files:
<?php
header('Content-type: application/json');
$files = scandir('.');
$images = array();
foreach ($files as $file)
{
switch(strtolower(substr(strrchr($file,'.'),1)))
{
case 'png':
case 'jpeg':
case 'jpg': $images[] = $file;
}
}
echo json_encode($images);
?>
You can then use NSURLConnection (or AFNetworking or whatever) to retrieve this and convert the JSON to a NSArray.
For example, using AFNetworking:
NSURL *url = [NSURL URLWithString:#"http://yourwebserver.com/some/path/images.php"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([responseObject isKindOfClass:[NSArray class]])
[self doSomethingWithImageNames:responseObject];
else
NSLog(#"expected array, received: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"AFHTTPRequestOperation error: %#", error);
}];
[op start];
Or NSURLConnection:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
NSLog(#"sendAsynchronousRequest error: %#", connectionError);
return;
}
NSError *jsonError = nil;
NSArray *imageNames = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
NSLog(#"JSONObjectWithData error: %#", jsonError);
return;
}
[self doSomethingWithImageNames:imageNames];
}];
If you have to rely upon the HTML response, while you generally shouldn't use regular expressions, in this limited use case, you can probably get away with it. In my case, my web server reports links to the files using filename syntax, so I can grab those href tags with something like:
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFHTTPResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (![responseObject isKindOfClass:[NSData class]]) {
NSLog(#"Was expecting `NSData` and got %#", responseObject);
return;
}
NSString *string = [[NSString alloc] initWithData:(NSData *)responseObject encoding:NSUTF8StringEncoding];
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"<a\\s[\\s\\S]*?href\\s*?=\\s*?['\"](.*?)['\"][\\s\\S]*?>"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSMutableArray *results = [NSMutableArray array];
[regex enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[results addObject:[string substringWithRange:[result rangeAtIndex:1]]];
}];
[self doSomethingWithImageNames:results];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"AFHTTPRequestOperation error: %#", error);
}];
[op start];

JSON to Objective-C Dictionary

I'm making URL Request to an API but I dont know how to render the JSON, It generates an array of multiple users like this [{"user": "value"}, {"user":"value"}] and I was trying to use a TableView so I need an NSDictionary but i think is better to render a JSON like {users: [{"user": "value"}, {"user":"value"}]}. I have this code to make the request
#import "JSONKit.h"
NSError *error = nil;
NSURLResponse *response = nil;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"http://localhost:3000/getusers"]];
[request setHTTPMethod:#"GET"];
NSData *jsonData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
users = [[jsonData objectFromJSONData] objectForKey:#"users"];
usersKeys = [users allKeys];
but I'm getting this error
2012-09-16 18:51:11.360 tableview[2979:c07] -[JKArray allKeys]: unrecognized selector sent to instance 0x6d30180
2012-09-16 18:51:11.362 tableview[2979:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[JKArray allKeys]: unrecognized selector sent to instance 0x6d30180'
I dont really know how to accomplish this so any help is useful, thanks
You are getting that error because whatever got parsed out of "jsonData" isn't necessarily what you expected (i.e. a dictionary).
Perhaps you need some error checking in that code of yours.
For example:
NSData *jsonData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(jsonData)
{
id objectReturnedFromJSON = [jsonData objectFromJSONData];
if(objectReturnedFromJSON)
{
if([objectReturnedFromJSON isKindOfClass:[NSDictonary class]])
{
NSDictionary * dictionaryFromJSON = (NSDictionary *)objectReturnedFromJSON;
// assuming you declared "users" & "usersKeys" in your interface,
// or somewhere else in this method
users = [dictionaryFromJSON objectForKey:#"users"];
if(users)
{
usersKeys = [users allKeys];
} else {
NSLog( #"no users in the json data");
}
} else {
NSLog( #"no dictionary from the data returned by the server... check the data to see if it's valid JSON");
}
} else {
NSLog( #"nothing valid returned from the server...");
}
} else {
NSLog( #"no data back from the server");
}
I was thinking on something like this
NSError *error = nil;
NSURLResponse *response = nil;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: #"http://localhost:3000/getusers"]];
[request setHTTPMethod:#"GET"];
NSData *jsonData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
JSONDecoder *decoder = [[JSONDecoder alloc]
initWithParseOptions:JKParseOptionNone];
NSArray *json = [decoder objectWithData:jsonData];
NSMutableArray *objects = [[NSMutableArray alloc] init];
NSMutableArray *keys = [[NSMutableArray alloc] init];
for (NSDictionary *user in json) {
[objects addObject:[user objectForKey:#"user" ]];
[keys addObject:[user objectForKey:#"value" ]];
}
users = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
NSLog(#"users: %#", users);
usersKeys = [users allKeys];
But it doesnt look efficient for many items or im wrong?