working with json data - iphone

I have the follow code that parses JSON data received from a server:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
NSArray *array_webdata=[[NSArray array] init];
NSString *searchStatus = [[NSString alloc] initWithData:webData encoding:NSUTF8StringEncoding];
array_webdata = [parsedata objectWithString:searchStatus error:nil];
NSDictionary *usersList = [array_webdata valueForKey:#"results"];
//I think that is not a real NSDictionary because if I write NSArray *keys = [usersList allKeys]; the execution crashes
NSLog(#"\n usersList =\n %# \n", usersList);
[searchStatus release];
[connection release];
[webData release];
[pool drain];}
the json data stored in usersList has the structure:
(
{
createTime = "date hour";
fullname = "user name";
"prof_id" = number;
thumb = "image.jpg";
},
{
data of rest of users...
}
)
And I would like create a class to store the data of each user and use "prof_id" when I want to use a particular use.
I need this because the app needs a list with all users (not tableview) and I think this is de easiest way.
Can someone help me? Thanks!!

Please used JsonKit Framework to parse json data received from web service.
Read data and parse using JSONKit:
NSData* jsonData = [NSData dataWithData:webData];
JSONDecoder* decoder = [[JSONDecoder alloc]
initWithParseOptions:JKParseOptionNone];
NSArray* json = [decoder objectWithData:jsonData];
After that, you'll have to iterate over the json variable using a for loop.
Create new class with the name User (file->new->file) inherited from NSObject class, create required parameters in .h/.m file.(do synthesize to generate getter/setter for attributes)
import User.h in your connection class and create objects of User entity in iterator loop and add those object in global scope array.
for(NSDictionary *userInfo in json) {
User* user=[[User alloc] init];
user.fullName=[userInfo valueForKey:#"fullname"];
user.prof_id=[[userInfo valueForKey:#"prof_id"] integerValue];
// Add into your global array
[usersList addObject:user];
[user release];// if ARC is not enable
}
// Check for successful object creation
NSLog(#"USER LIST contain User class Objects- %#",userList);

if i'm not wrong the only thing you need to do is :
NSMutableArray *yourArray = usersList;
and then with a for loop like
for(int i = 0;i<[usersList count] ;i++)
{
NSMutableDictionary *yourDictionary = [usersList objectAtIndex:i];
int prof_id = [yourDictionary valueForKey:#"prof_id"];
}
you can get your prof_id like that.
i hope this helps...

Use JSON Framework, and parse data using below code.
NSString* newStr = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"yout link to json file"] encoding:NSUTF8StringEncoding error:nil];
NSLog(#"new str - %#",newStr);
NSArray *response = [newStr JSONValue];
NSLog(#"json array - %#",response);
Use the response array to show your results.

Related

fetch JSON data asynchronously

I want to fetch JSON data asynchronously. The data is set up in a way that one request will bring only 8 records. I need to send the requests repeatedly until the response becomes empty or returns less than 8 records.
Currently, I have these methods in myviewcontroller.m class:
(void)myCallback:(id)sender {
MyDataRequest *objMyDataRequest = [[[MyDataRequest alloc] init] autorelease];
objMyDataRequest.myRequiredVariableToGetAuthTokenDataResponse = classOfMyCallBack.someVariable;
// Initiate getAuthToken request
[objWishListRequest initiateGetAuthTokenRequest:self requestSelector:#selector(getAuthTokenDataResponse:)];
}
Now here is the definition of getAuthTokenDataResponse:
(void) getAuthTokenDataResponse:(NSData *)data {
NSString *stringResponse = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *objDictionaryForStringResponse = [parser objectWithString:stringResponse];
[stringResponse release];
[parser release];
MyListRequest *objMyListRequest = [[[MyListRequest alloc] init] autorelease];
objMyListRequest.myRequiredValueToGetMyDataResponse = [objDictionaryForStringResponse objectForKey:#"Data"];
// Initiate GetMyDataResponse request
[objMyListRequest initiateGetMyDataRequest:self requestSelector:#selector(getMyDataResponse:)];
}
(void) getMyDataResponse:(NSData *)data {
NSString *stringResponse = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *objGetMyDataRootDictionary = [parser objectWithString:stringResponse];
[stringResponse release];
[parser release];
NSDictionary *dataElements=[objGetMyDataRootDictionary objectForKey:#"Data"];
Wish *objMyData;
for (NSDictionary* objGetMyDataRootDictionary in dataElements) {
objMyData = [[Wish alloc]init];
//add different elements from dataElements into member variables of object objWish
[self.myDataArray addObject:objMyData];
[objMyData release];
}
[self.myDataTableView reloadData];
}
This method lies in MyDataRequest class:
(void)initiateGetMyDataRequest:(id)requestDelegate requestSelector:(SEL)requestSelector{
// Set the delegate and selector
self.delegate = requestDelegate;
self.callback = requestSelector;
NSString* unescapedUrlString = [NSString stringWithFormat:#"http://test.mytesturl.com/core.svc/alldata/My/get/All/?token=%#&search=&page=1",myRequiredtokenparameter];
[self request:url];
}
I need to send multiple requests to the same url (with different parameter value i.e. value of page number) to fetch the results. How may I achieve it given the above scenario? The calls must be asynchronous.
How should I make the actual flow between all these calls? How may I get the data of "all the pages" asynchronously?
I think you are looking for a operation queue. I use ASIHTTPRequests in my apps and they work.
If you want to use this library, here's the link how to use it: Show UIActivityIndicatorView when loading NSString from Web

help on sorting NSMutableDictionary strings that are URL images

i want to populate the tablecell with title and imageurl from xml list.
i manage to store the title (NSMutableDictonary *sections )and imageURL (NSMutableDictonary *sectionsImg) into 2 NSMutableDictionary respectively.
/*******This is in viewDidLoad***/
Directory *allDirectory = [appDelegate.directories objectAtIndex:0];
for (allDirectory in appDelegate.directories)
{
NSDictionary *dica = [NSDictionary dictionaryWithObject:allDirectory.dirTitle forKey:#"dirTitle"];
NSDictionary *dico = [NSDictionary dictionaryWithObject:allDirectory.imageURL forKey:#"imageURL"];
[dirName addObject:dica];
[dirImage addObject:dico];
//NSLog(#"dic of items : %#",dirImage);
}
for (allDirectory in appDelegate.directories)
{
//retrieve the first letter from every directory title (dirTitle)
NSString * c = [allDirectory.dirTitle substringToIndex:3];
NSString * m = allDirectory.imageURL;
found = NO;
find = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
for (NSString *stra in [self.sectionsImg allKeys])
{
if([stra isEqualToString:m])
{
find = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc]init] forKey:c ];
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
if (!find)
{
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
}
for (NSDictionary *directory in dirName)
{
[[self.sections objectForKey:[[directory objectForKey:#"dirTitle"] substringToIndex:3]] addObject:directory];
//NSLog(#"hehehe have : %#",sections);
}
for (NSDictionary *directoryImg in dirImage)
{
//[[self.sectionsImg objectForKey:[[directoryImg objectForKey:#"imageURL"] substringFromIndex:0]] addObject:directoryImg];
[[self.sectionsImg objectForKey:[directoryImg objectForKey:#"imageURL"]] addObject:directoryImg];
//NSLog(#"HOHOHO have : %#",sectionsImg);
}
And on cellForRowAtIndexPath i declare a dictionary
NSDictionary *dictionary = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = [dictionary objectForKey:#"dirTitle"];
but when i tried to declare a dictionary for imageURL
NSDictionary *dictionaryImg = [[self.sectionsImg valueForKey:[[[self.sectionsImg allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
it gives me a error :
Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
any idea why? the logic is supposed to be the same where xml title and url can be retrieve and be displayed. Title is retrievable but imageURL is not. Help is deeply appreciated !
You are trying to sort an array... except for the fact your array isn't an array, but a NSDictionary.
Your code isn't the best at the moment. Your getting the idea of Dictionaries wrong and may be confusing them with arrays, so my best guess is your quite new to programming into objective-c.
You have two lists of things, if I'm not mistaken. The first list is the list of names, and the second list is an image corresponding with that name.
Below I'm going to do two things:
Firstly, I'm giving you two ways on how to fix your problem. It has a sample code included and gives you a small explanation with it. The possibility exist you don't understand parts of what I describe. In that case, you should;
Check out the link I described below the two solutions. It has a tutorial which makes you understand everything about arrays, dictionaries, tables and, as a bonus, XML-parsing.
So, in my opinion, you can do two things:
The first one is using an array of NSDictionaries. You'd be using a code which looks like:
NSMutableDictionary *itemOne = [[NSMutableDictionary alloc] init];
NSMutableDictionary *itemTwo = [[NSMutableDictionary alloc] init];
NSMutableArray *listOfAll = [[NSmutableArray alloc] init];
NSString *itemOneName = [[NSString alloc] initWithFormat:#"This is picture 1"];
NSString *itemTwoName = [[NSString alloc] initWithFormat:#"This is picture 2"];
NSData *imageOneData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic1.jpg"]];
NSData *imageTwoData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic2.jpg"]];
UIImage *itemOneImage = [UIImage imageWithData: imageOneData];
UIImage *itemTwoImage = [UIImage imageWithData: imageTwoData];
[itemOne setObject:itemOneNameString forKey:#"Name"];
[itemOne setObject:itemOneImage forKey:#"Image"];
[itemTwo setObject:itemTwoNameString forKey:#"Name"];
[itemTwo setObject:itemTwoImage forKey:#"Image"];
[listOfAll addObject:itemOne];
[listOfAll addObject:itemTwo];
Anything can be filled using that array. Just use something with a for-loop to iterate through your array.
for (int i = 0; i < [listOfAll count]; i++)
{
NSMutableDictionary *currentItem = [[NSMutableDictionary alloc] initWithDictionary:[listOfAll objectAtIndex:i]];
//Do something with that current item
}
You can also use that index in your tableView. In that case, you have to use your variable section instead of i to get your desired index.
The second one is using two arrays. Imagine you get an image named imageOne with the text imageName. Then you should use:
NSMutableArray *nameList = [[NSMutableArray alloc] init];
[nameList addObject: imageName];
NSMutableArray *imageList = [[NSMutableArray alloc] init];
[imageList addObject: imageOne];
If you want to use a certain item out of those lists, you just have to use the same indexnumber.
For example:
[theTitleLabel setText:[[NSString alloc] initWithFormat:#"%#", [nameList objectAtIndex:x]]];
[theImageView setImage:[imageList objectAtIndex:x]];
Make sure the x's are the same number.
I understand this is all a lot of information, especially if you're new to Objective - C. A tutorial exists which gives you a lot of information about how to use arrays, dictionaries and table views. As a bonus, you get to know a little about XML-parsing.
I suggest you walk through that tutorial and do everything and read everything it says. This should give you a nice start into the world of programming in iPhones.
Good luck!

Adding 2 keyvalues to list from JSON object

I want to append 2 key values from JSON object to my list in iPhone app. Below is my code for that,
SBJsonParser *jsonParser = [[[SBJsonParser alloc] init] autorelease];
NSString *jsonString=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://test/json/json_data.php"]];
id response = [jsonParser objectWithString:jsonString error:NULL];
NSDictionary *feed = (NSDictionary *)response;
list = (NSArray *)[feed valueForKey:#"fname"];
the above code properly displays the value from fname but what do i do if i want to add lname to it. for eg, my object is
[{"fname":"Bill","lname":"Jones"},{"fname":"John","lname":"Jacobs"}]
i want to display names as Bill Jones, John Jacobs and so on in the list. Currently it only displays Bill, John..I tried doing something like #"fname"#lname but it wont work..Can anybody please help me..
An observation: the response from the JSON parser is not a dictionary, but an array given the string you pass in. Your code works because -valueForKey: is something an array will respond to. The array sends -valueforKey: to each element and builds an array out of the responses.
There are two ways you can do what you want (at least)
Iterate through the array explicitly
NSMutableArray* list = [[NSMutableArray alloc] init];
for (id anObject in response)
{
[list addObject: [NSString stringWithFormat: #"%# %#",
[anObject objectForKey: #"fName"],
[anObject objectForKey: #"lname"]]];
}
Add a category to NSDictionary
#interface NSDictionary(FullName)
-(NSString*) fullName;
#end
#implementation NSDictionary(FullName)
-(NSString*) fullName
{
return [NSString stringWithFormat: #"%# %#",
[self objectForKey: #"fName"],
[self objectForKey: #"lname"]];
}
#end
Then your existing code changes to
list = (NSArray *)[feed valueForKey:#"fullName"];

How to extract the actual NSString from json object as NSArray

I'm working with a large set of json and really just need the NSString representation of what's inside the NSArray -including all the { }
My question is this - is their a better way than simply looping through each NSArray inside the main NSArray and outputting the description one by one?
ie- the below is a start to this process but it's very brittle meaning I need to know each item inside the hat {} and this isn't something I actually care about. I just need the json string to move forward.
The working code is below (thank you in advance!)
NSString* responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray* json = [responseString JSONValue];
NSArray* item = [json valueForKeyPath:#"d.data"];
NSArray* hatjson = [item objectForKey:#"hat"];
NSMutableString * result = [[NSMutableString alloc] init];
for (NSObject * obj in hatjson)
{
[result appendString:[obj description]];
}
NSLog(#"the hat json is .. %#", result);
// …
NSArray* hatjson = [item objectForKey:#"hat"];
NSString *result = [hatjson JSONRepresentation];
NSLog(#"the hat json is .. %#", result);
I’m assuming you’re using SBJSON for JSON parsing. SBJSON defines a category on NSObject that includes the method
- (NSString *)JSONRepresentation;
This method returns a string with the JSON representation of a given object so long as the object is an instance of a class which SBJSON can convert to JSON (e.g. strings, numbers, arrays, dictionaries).
I'm assuming you're using the JSON library from here: https://github.com/stig/json-framework
You're complaining that the code you provided is brittle, but it sounds like, for what you want, the situation is brittle, so I think it's ok for the code that access it to be brittle, as long as you put NSAsserts in there so that you know ASAP when your assumptions have been broken.
I think the most brittle aspect of the code you've shown is that it assumes you're getting back NSArrays, when it appears from how you're accessing it that it's actually giving you NSDictionaries.
For instance, reading your code, I conclude that the responseString represents a JSON nested map looking something like this:
{ "d": { "data": { "hat": "baseball cap" } } }
The question then is "do you ever expect the value corresponding to the "hat" key to ever have more than one value?" I would genericize this code like so:
NSString* responseString = [[[NSString alloc] initWithData: responseData encoding: NSUTF8StringEncoding] autorelease];
[responseData release];
id json = [responseString JSONValue];
id hatJSONValue = [json valueForKeyPath:#"d.data.hat"];
NSString* result = nil;
if ([hatJSONValue isKindOfClass: [NSArray class]] && [hatJSONValue count] == 1)
{
result = [[hatJSONValue lastObject] description];
}
else
{
NSAssert(NO, #"Assumptions about returned JSON were wrong.");
}
NSLog(#"the hat json is .. %#", result);
Generally speaking, you always have to make tradeoffs between writing non-brittle code and getting things done. The key should be that if your code is going to make assumptions, you should assert that they're true, so if the situation ever changes, you'll know!

Moving through JSON data in iPhone app

I'm afraid I'm a newbie to objective-c programming, and I am having a problem that I have spent all day trying to figure out and I cannot, so I am humbly asking you guys if anyone can help.
I am trying to read the details from a JSON page online (for instance a local services directory) and have installed the JSON library into Xcode and it seems to work fine. I'm developing for the iPhone by the way, and have the latest versions all installed.
The problem is, what with me being a newb and all, I seem unable to retrieve all the information I need from the JSON file.
the JSON data I am testing with is this:
"testlocal_response" = {
header = {
query = {
business = newsagent;
location = brighton;
page = 1;
"per_page" = 1;
"query_path" = "business/index";
};
status = ok;
};
records = (
{
address1 = "749 Rwlqsmuwgj Jcyv";
address2 = "<null>";
"average_rating" = 0;
"business_id" = 4361366;
"business_keywords" = "<null>";
"business_name" = "Gloucester Newsagents";
"data_provider_id" = "<null>";
"display_details" = "<null>";
"distance_in_miles" = "0.08";
fax = "<null>";
gridx = "169026.3";
gridy = "643455.7";
"image_url" = "<null>";
latitude = "50.82718";
"logo_path" = Deprecated;
longitude = "-0.13963";
phone = 97204438976;
postcode = "IY1 6CC";
"reviews_count" = 0;
"short_description" = "<null>";
"touch_url" = "http://www.test.com/business/list/bid/4361366";
town = BRIGHTON;
url = "<null>";
}
);
};
}
Now, in my code ViewController.m page, in the 'connectionDidFinishLoading' area I have added:
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
// make sure JSON has all been pulled in
NSLog(#"This is from the JSON page:\n");
NSLog(responseString);
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
// using either of these seems to make no difference?
NSDictionary *touchDirect = [json objectWithString:responseString error:&error];
//NSArray *touchDirect = [json objectWithString:responseString error:&error];
[responseString release];
if (touchDirect == nil)
label.text = [NSString stringWithFormat:#"JSON parsing failed: %#", [error localizedDescription]];
else {
NSMutableString *text = [NSMutableString stringWithString:#"Test directory details:\n"];
[text appendFormat:#"%#\n", [[[[touchDirect objectForKey:#"testlocal_response"] objectForKey:#"header"] objectForKey:#"query"] objectForKey:#"business"]];
label.text = text;
Testing this I get the value for the business ('newsagent') returned, or location ('brighton') which is the correct. My problem is, I cannot go further into the JSON. I don't know how to pull out the result for the actual 'records' which, in the test example there is only one of but can be more divided using brackets '(' and ')'.
As soon as I try to access the data in these record areas (such as 'address1' or 'latitude' or 'postcode') it fails and tells me 'unrecognized selector sent to instance'
PLEASE can anyone tell me what I'm doing wrong?! I've tried so many different things and just cant get any further! I've read all sorts of different things online but nothing seems to help me.
Any replies deeply appreciated. I posted up a question on iPhone SDK too but havent had a useful response yet.
many thanks,
-Robsa
Have you validated your JSON?
It's not clear how you are trying to access the objects that you say are erroring. The specific line(s) you are having trouble with would be helpful.
It's usually easier to set a pointer to the dictionary you are going to be accessing for readability..
NSDictionary *records = [[objectForKey:#"testlocal_response"] objectForKey#"records"];
then...
NSString *businessName = [records objectForKey:#"business_name"];
float latitude = [[records objectForKey:#"latitude"] floatValue];
Well, I finally sorted it out! I put the records in an NSString, which I could then access using objectAtIndex.
Here is the main Viewcontroller.m code for it:
- (void)viewDidLoad {
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"*URL TO JSON DATA HERE*"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSMutableArray *touchDirect = [json objectWithString:responseString error:&error];
NSString *touchRecord = [[touchDirect objectForKey:#"touchlocal_response"] objectForKey:#"records"];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
label.text = [NSString stringWithFormat:#"Connection failed: %#", [error description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
//Retrieves the JSON header Data, returning info such as 'status' [which should return 'ok']
NSMutableArray *touchDirect = [json objectWithString:responseString error:&error];
//Puts the records returned into a string called touchRecord
NSString *touchRecord = [[touchDirect objectForKey:#"touchlocal_response"] objectForKey:#"records"];
[responseString release];
// test results
[text appendFormat:#" Address: %#\n", [[touchRecord objectAtIndex:0] objectForKey:#"address1"]];
[text appendFormat:#" Phone: %#\n", [[touchRecord objectAtIndex:0] objectForKey:#"phone"]];
[text appendFormat:#" Address Data record 2: %#\n", [[touchRecord objectAtIndex:1] objectForKey:#"address1"]];
[text appendFormat:#" Phone Data record 2: %#\n", [[touchRecord objectAtIndex:1] objectForKey:#"phone"]];
This now seems to work fine. I also have a if..else if statement to catch errors now. Does this code look Ok?
Thanks for the tip, Nick - I was just trying to get the output right before tidying the code up. I am using an NSMutableArray to put my JSON into initially, is this OK? What is the benefit of putting it into an NSDictionary?
regards,
Robsa