AFNetworking and compex JSON structures - objective-c

I'm writing a native application for iPhone, I'm quite new to the subject.
Using AFNetworking I request (with POST) and process the JSON reply.
But I've noticed that once the JSON response is more complex:
{
"isFound": "YES",
"timestamp": "2013-06-12 22:46:47",
"screenTitle": "Perla Review",
"placeName": "Perla",
"placeUniqueId": "101",
"placeCategory": "PUB",
"username": "#jim",
"userImgURL": "",
"gender": "male",
"infoMsg": "TBD",
"youLike": "Like",
"likesInfoMsg": "",
"revInfoList": [
{
"type": 0,
"data": "",
"text": ""
},
{
"type": 1,
"data": "2",
"text": ""
},
{
"type": 2,
"data": "3",
"text": ""
}
],
"commentsList": []
}
Then AFNetworking fails to read and construct the sub arrays (revInfoList & commentsList).
Am I doing something wrong, or AFNetworking does not support such json structures?
Here is my objective C code to request the data and process the reply:
static NSString *const myAPIBaseURL = #"http://somedomain.com/api/";
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:myAPIBaseURL]];
NSString *reqPath = #"review/review_fullinfo";
// prepare params
NSString *reqData_loginUsername = [[[Storage_Modal alloc] init] getLoginUsername];
NSString *reqData_currentCoordinatesLat = [[NSNumber numberWithDouble:[appDelegateInst.myCurrentLocation coordinate].latitude] stringValue];
NSString *reqData_currentCoordinatesLon = [[NSNumber numberWithDouble:[appDelegateInst.myCurrentLocation coordinate].longitude] stringValue];
NSString *reqData_reviewUniqueId = reviewUniqueId;
NSDictionary *parameters = [[NSDictionary alloc] initWithObjectsAndKeys:
#"json", #"format",
reqData_loginUsername, #"loginUsername",
reqData_currentCoordinatesLat, #"currentCoordinatesLatitude",
reqData_currentCoordinatesLon, #"currentCoordinatesLongitude",
reqData_reviewUniqueId, #"reviewUniqueId",
nil];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
[client registerHTTPOperationClass:[AFJSONRequestOperation class]];
[client setDefaultHeader:#"Accept" value:#"application/json"];
[client postPath:reqPath parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *xxx = responseObject;
// 'xxx' contains all the top level keys and values, but the sub arrays are empty... why?
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"SERVER ERROR: %#", error);
}
**NOTE: Please forgive me if there is a syntax error in the objective-C code, I had to collect the code from multiple functions.
Thank you.

AFNetworking uses NSJSONSerialization to convert your JSON into a Foundation object, usually an NSDictionary or NSArray. NSJSONSerialization conforms strictly to RFC 4627.
The JSON response you've posted is valid JSON, so there are only 3 causes I can think of for the behavior you describe:
Your server is returning a different payload than the one you posted here
Your server is encoding the payload incorrectly
You're incorrect about what the xxx NSDictionary contains
You can test #1 by looking at your AFJSONRequestOperation's responseString and responseData properties. #2 and #3 can both be tested by inspecting the xxx object carefully. If you post the output of these objects here, we can help you diagnose better.
Your AFNetworking code and your expected JSON both look fine, so I don't think it's an issue with AFNetworking.

Related

Retrieve nested objects from JSON - Objective C

I seem to be having a small issue with the parsing through a nested JSON result. The following code works perfectly if the JSON is not nested. I'm a littler perplexed on how to proceed as every attempted (through examples of others) have failed.
So, to test this I'm using the following API from https://developer.worldweatheronline.com/page/explorer-free
I simply would like to get my current temperature (temp_c).
Below is the code calling the service. Note that I have an NSObject that will fill the data, but of course I can't seem to get to that stage. Also it is an NSMutableArray throughout. Again, I don't think that is the issue but provides context.
-(void)retrieveLocalWeatherService {
NSURL *url = [NSURL URLWithString:getLocalWeather];
NSData *data = [NSData dataWithContentsOfURL:url];
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//set up array and json call
weatherArray = [[NSMutableArray alloc]init];
//Loop through the JSON array
for (int i = 0; i< jsonArray.count; i++)
{
//create our object
NSString *nTemp = [[jsonArray objectAtIndex:i]objectForKey:#"temp_C"];
NSString *nPressure = [[jsonArray objectAtIndex:i]objectForKey:#"pressure"];
NSString *nHumidity = [[jsonArray objectAtIndex:i]objectForKey:#"humidity"];
//Add the object to our animal array
[weatherArray addObject:[[LocalWeather alloc]initWithtemp:(nTemp) andpressure:nPressure andhumidity:nHumidity]];
}
Here is the JSON response.
{
"data": {
"current_condition": [
{
"cloudcover": "75",
"FeelsLikeC": "31",
"FeelsLikeF": "88",
"humidity": "70",
"observation_time": "05:15 AM",
"precipMM": "0.0",
"pressure": "1011",
"temp_C": "28",
"temp_F": "82",
"visibility": "10",
"weatherCode": "116",
"weatherDesc": [
{
"value": "Partly Cloudy"
}
],
"weatherIconUrl": [
{
"value": "http://cdn.worldweatheronline.net/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png"
}
],
"winddir16Point": "N",
"winddirDegree": "10",
"windspeedKmph": "41",
"windspeedMiles": "26"
}
],
"request": [
{
"query": "Brisbane, Australia",
"type": "City"
}
],
I cut off the JSON service as it goes for miles, so where am I going wrong? I believe its somewhere within "for-loop" but am unsure where. I know its major node is "data" and then sub-node is "current_condition". Should I be digging through the JSON results? If what is the best approach.
BTW, I'm getting a response from the server with the entire JSON result..clearly a parsing issue on my part.
Thanks in advance!
please be kind i'm a newbie.
You are parsing your JSON data in wrong way, you are parsing JSON directly to Array but as per your JSON format your JSON will return an NSDictionary not NSArray.
-(void)retrieveLocalWeatherService {
NSURL *url = [NSURL URLWithString:getLocalWeather];
NSData *data = [NSData dataWithContentsOfURL:url];
NSDictionary *weatherJson = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSArray *currentConditionArray = [weatherJson valueForKeyPath:#"Data.current_condition"];
//set up array and json call
weatherArray = [[NSMutableArray alloc]init];
//Loop through the JSON array
for (NSDictionary *item in currentConditionArray)
{
//create our object
NSString *nTemp = [item objectForKey:#"temp_C"];
NSString *nPressure = [item objectForKey:#"pressure"];
NSString *nHumidity = [item objectForKey:#"humidity"];
//Add the object to our animal array
[weatherArray addObject:[[LocalWeather alloc]initWithtemp:(nTemp) andpressure:nPressure andhumidity:nHumidity]];
}
}

POSTING json with nested objects to server

This is my first native iOS app, so please bear with..
How would I construct this json data in a NSDictionary (I would guess thats how I would do it) so I cand make it part of my request body.
{
"Properties":{
"Description":"String content",
"Domain":"String content",
"GroupID":"String content",
...
},
"Foo":{....},
}
Yes, use a dictionary, created using literals or code.
NSDictionary* jsonDict = #{#"Properties":#{#"Description":#"String content",#"Domain":#"String content",#"GroupID":#"String content",},#"Foo":{....},}
Convert the dictionary into JSON data ready for posting.
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0 error:&error];

Using JSON in iOS before server is online - include in file as string

I'm working on an application in iOS that gets some of its information from a server using JSON. The server's not online yet, so I'm trying to use what the developers working on the server have given me as sample code to build from. I thought the easiest way to do this would be storing the JSON response in a string and using NSJSONSerialization.
The code I'm trying looks as follows:
NSString * JSONString = #"{\"firstName\":\"John\", \"lastName\": \"Smith\", \"age\": 25, \"address\": {\"streetAddress\": \"21 2nd Street\",\"city\": \"New York\", \"state\": \"NY\",\"postalCode\": \"10021\"},}";
bool valid = [NSJSONSerialization isValidJSONObject:JSONString];
if (valid) {
NSLog(#"Valid JSON");
} else {
NSLog(#"Invalid JSON");
}
Which always logs, "Invalid JSON."
All of my research has given resources about how to get the data from a server, but nothing about testing before the server is available. Any ideas?
Two issues. First, your JSON string has an extra comma. It should be:
NSString *jsonString = #"{\"firstName\":\"John\", \"lastName\": \"Smith\", \"age\": 25, \"address\": {\"streetAddress\": \"21 2nd Street\",\"city\": \"New York\", \"state\": \"NY\",\"postalCode\": \"10021\"}}";
Second, though, your original code has a false negative. A string will always fail isValidJSONObject. That method is not for validating a JSON string. If you want to use isValidJSONObject, you should pass it a NSDictionary, e.g.:
NSDictionary* jsonDictionary = #{
#"firstName" : #"John",
#"lastName" : #"Smith",
#"age" : #(25),
#"address" : #{
#"streetAddress": #"21 2nd Street",
#"city" : #"New York",
#"state" : #"NY",
#"postalCode" : #"10021"
}
};
BOOL valid = [NSJSONSerialization isValidJSONObject:jsonDictionary];
if (valid) {
NSLog(#"Valid JSON");
} else {
NSLog(#"Invalid JSON");
}
So, the best way to create a JSON string is to create the dictionary like above, and then invoke dataWithJSONObject. I generally would advise against writing a JSON string manually, because you can always introduce typos like your extra comma. I always build a JSON string from a NSDictionary like this, because you never have to worry about whether the string is well formed or not. NSJSONSerialization takes care of the hard work of formatting the string properly:
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary
options:0
error:&error];
if (error)
NSLog(#"dataWithJSONObject error: %#", error);
NSString *jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding]);
NSLog(#"JSON string is: %#", jsonString);
That yields:
{"age":25,"lastName":"Smith","firstName":"John","address":{"streetAddress":"21 2nd Street","state":"NY","city":"New York","postalCode":"10021"}}
Or, if you use the NSJSONWritingPrettyPrinted option to dataWithJSONObject:
{
"age" : 25,
"lastName" : "Smith",
"firstName" : "John",
"address" : {
"streetAddress" : "21 2nd Street",
"state" : "NY",
"city" : "New York",
"postalCode" : "10021"
}
}
I test by keeping a file in my resources say, test.json. You can open it with the following code:
NSString *path = [NSBundle.mainBundle pathForResource:#"test.json" ofType:#"json"];
NSError *error = nil;
NSString *fileContents = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&error];
Then call the method to convert to a JSON object. The test file will be more readable than what you have above. Hope this helps!

Best approaches on "filling" a json file , iOS?

I am working on an application and i need to send some data to my server. The data is in json format and more specific the json file looks like this :
{
"eventData": {
"eventDate": "Jun 13, 2012 12:00:00 AM",
"eventLocation": {
"latitude": 43.93838383,
"longitude": -3.46
},
"text": "hjhj",
"imageData": "raw data",
"imageFormat": "JPEG",
"expirationTime": 1339538400000
},
"type": "ELDIARIOMONTANES",
"title": "accIDENTE"
}
So i have tried to hardcode the data in my json file and everything works ok. Now what i am trying to do is to fill my json file , using variables so everything can work automatcally when data changes. What would a good approach be for that?? Some sample code would be highly appreciated as i am very new to obj-c. Thanks for ur time! :D
EDIT
Ok so an NSDictionary seems a nice way to go.
But how can i create a dictionary to look like the json format?? I ve only used dictionaries like this :
NSArray *keys = [NSArray arrayWithObjects:#"eventDate", #"eventLocation", #"latitude" nil];
NSArray *objects = [NSArray arrayWithObjects:#"object1", #"object2", #"object3", nil];
dictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
For the langitude and longitude for example it is a pair of key and value but for the rest??
All you need is a NSDictionary containing your keys and values. Since iOS5, you can proceed with the following code
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:myDictionary
options:NSJSONWritingPrettyPrinted
error:&error];
if (!jsonData) {
NSLog(#"Got an error: %#", error);
} else {
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// ...
}
I have used this library . It is very simple and useful. And for tutorial check this site.

How do I parse JSON with Objective-C?

I am new to iPhone. Can anyone tell me the steps to follow to parse this data and get the activity details, first name, and last name?
{
"#error": false,
"#data": {
"": {
"activity_id": "35336",
"user_id": "1",
"user_first_name": "Chandra Bhusan",
"user_last_name": "Pandey",
"time": "1300870420",
"activity_details": "Good\n",
"activity_type": "status_update",
"photo_url": "http://184.73.155.44/hcl-meme/QA_TEST/sites/default/files/pictures/picture-1627435117.jpg"
},
"boolean": "1",
"1": {
"1": {
"photo_1_id": "9755"
},
"activity_id": "35294",
"album_name": "Kalai_new_Gallery",
"user_id": "31",
"album_id": "9754",
"user_first_name": "Kalaiyarasan",
"user_last_name": "Balu",
"0": {
"photo_0_id": "9756"
},
"time": "1300365758",
"activity_type": "photo_upload",
"photo_url": "http://184.73.155.44/hcl-meme/QA_TEST/"
},
"3": {
"activity_id": "35289",
"user_id": "33",
"user_first_name": "Girija",
"user_last_name": "S",
"time": "1300279636",
"activity_details": "girija Again\n",
"activity_type": "status_update",
"photo_url": "http://184.73.155.44/hcl-meme/QA_TEST/sites/default/files/pictures/picture-33-6361851323080768.jpg"
},
"2": {
"owner_first_name": "Girija",
"activity_id": "35290",
"activity_details": "a:2:{s:4:\"html\";s:51:\"!user_fullname and !friend_fullname are now friends\";s:4:\"type\";s:10:\"friend_add\";}",
"activity_type": "friend accept",
"owner_last_name": "S",
"time": "1300280400",
"photo_url": "http://184.73.155.44/hcl-meme/QA_TEST/sites/default/files/pictures/picture-33-6361851323080768.jpg",
"owner_id": "33"
},
"4": {
"activity_id": "35288",
"user_id": "33",
"user_first_name": "Girija",
"user_last_name": "S",
"time": "1300279530",
"activity_details": "girija from mobile\n",
"activity_type": "status_update",
"photo_url": "http://184.73.155.44/hcl-meme/QA_TEST/sites/default/files/pictures/picture-33-6361851323080768.jpg"
}
}
}
With the perspective of the OS X v10.7 and iOS 5 launches, probably the first thing to recommend now is NSJSONSerialization, Apple's supplied JSON parser. Use third-party options only as a fallback if you find that class unavailable at runtime.
So, for example:
NSData *returnedData = ...JSON data, probably from a web request...
// probably check here that returnedData isn't nil; attempting
// NSJSONSerialization with nil data raises an exception, and who
// knows how your third-party library intends to react?
if(NSClassFromString(#"NSJSONSerialization"))
{
NSError *error = nil;
id object = [NSJSONSerialization
JSONObjectWithData:returnedData
options:0
error:&error];
if(error) { /* JSON was malformed, act appropriately here */ }
// the originating poster wants to deal with dictionaries;
// assuming you do too then something like this is the first
// validation step:
if([object isKindOfClass:[NSDictionary class]])
{
NSDictionary *results = object;
/* proceed with results as you like; the assignment to
an explicit NSDictionary * is artificial step to get
compile-time checking from here on down (and better autocompletion
when editing). You could have just made object an NSDictionary *
in the first place but stylistically you might prefer to keep
the question of type open until it's confirmed */
}
else
{
/* there's no guarantee that the outermost object in a JSON
packet will be a dictionary; if we get here then it wasn't,
so 'object' shouldn't be treated as an NSDictionary; probably
you need to report a suitable error condition */
}
}
else
{
// the user is using iOS 4; we'll need to use a third-party solution.
// If you don't intend to support iOS 4 then get rid of this entire
// conditional and just jump straight to
// NSError *error = nil;
// [NSJSONSerialization JSONObjectWithData:...
}
Don't reinvent the wheel. Use json-framework or something similar.
If you do decide to use json-framework, here's how you would parse a JSON string into an NSDictionary:
SBJsonParser* parser = [[[SBJsonParser alloc] init] autorelease];
// assuming jsonString is your JSON string...
NSDictionary* myDict = [parser objectWithString:jsonString];
// now you can grab data out of the dictionary using objectForKey or another dictionary method
NSString* path = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"json"];
//将文件内容读取到字符串中,注意编码NSUTF8StringEncoding 防止乱码,
NSString* jsonString = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//将字符串写到缓冲区。
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *jsonError;
id allKeys = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONWritingPrettyPrinted error:&jsonError];
for (int i=0; i<[allKeys count]; i++) {
NSDictionary *arrayResult = [allKeys objectAtIndex:i];
NSLog(#"name=%#",[arrayResult objectForKey:#"storyboardName"]);
}
file:
[
{
"ID":1,
"idSort" : 0,
"deleted":0,
"storyboardName" : "MLMember",
"dispalyTitle" : "76.360779",
"rightLevel" : "10.010490",
"showTabBar" : 1,
"openWeb" : 0,
"webUrl":""
},
{
"ID":1,
"idSort" : 0,
"deleted":0,
"storyboardName" : "0.00",
"dispalyTitle" : "76.360779",
"rightLevel" : "10.010490",
"showTabBar" : 1,
"openWeb" : 0,
"webUrl":""
}
]
JSON parsing using NSJSONSerialization
NSString* path = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"json"];
//Here you can take JSON string from your URL ,I am using json file
NSString* jsonString = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *jsonError;
NSArray *jsonDataArray = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&jsonError];
NSLog(#"jsonDataArray: %#",jsonDataArray);
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];
if(jsonObject !=nil){
// NSString *errorCode=[NSMutableString stringWithFormat:#"%#",[jsonObject objectForKey:#"response"]];
if(![[jsonObject objectForKey:#"#data"] isEqual:#""]){
NSMutableArray *array=[jsonObject objectForKey:#"#data"];
// NSLog(#"array: %#",array);
NSLog(#"array: %d",array.count);
int k = 0;
for(int z = 0; z<array.count;z++){
NSString *strfd = [NSString stringWithFormat:#"%d",k];
NSDictionary *dicr = jsonObject[#"#data"][strfd];
k=k+1;
// NSLog(#"dicr: %#",dicr);
NSLog(#"Firstname - Lastname : %# - %#",
[NSMutableString stringWithFormat:#"%#",[dicr objectForKey:#"user_first_name"]],
[NSMutableString stringWithFormat:#"%#",[dicr objectForKey:#"user_last_name"]]);
}
}
}
You can see the Console output as below :
Firstname - Lastname : Chandra Bhusan - Pandey
Firstname - Lastname : Kalaiyarasan - Balu
Firstname - Lastname : (null) - (null)
Firstname - Lastname : Girija - S
Firstname - Lastname : Girija - S
Firstname - Lastname : (null) - (null)
I recommend and use TouchJSON for parsing JSON.
To answer your comment to Alex. Here's quick code that should allow you to get the fields like activity_details, last_name, etc. from the json dictionary that is returned:
NSDictionary *userinfo=[jsondic valueforKey:#"#data"];
NSDictionary *user;
NSInteger i = 0;
NSString *skey;
if(userinfo != nil){
for( i = 0; i < [userinfo count]; i++ ) {
if(i)
skey = [NSString stringWithFormat:#"%d",i];
else
skey = #"";
user = [userinfo objectForKey:skey];
NSLog(#"activity_details:%#",[user objectForKey:#"activity_details"]);
NSLog(#"last_name:%#",[user objectForKey:#"last_name"]);
NSLog(#"first_name:%#",[user objectForKey:#"first_name"]);
NSLog(#"photo_url:%#",[user objectForKey:#"photo_url"]);
}
}