The blog post SAVING JSON TO CORE DATA has some great tips for converting a JSON response into a Core Data entity. What I want to do is a little bit more specific. I'd like to take a JSON response and convert the objects using the methods in the blog post into an NSObject with properties representing the response objects. The problem that I'm running into are nested properties of the object. Take this JSON response as an example:
http://us.battle.net/api/d3/profile/rnystrom-1254/
Using the methods described in the blog post, simple properties like "name" and "level" are easy to convert to NSString and NSNumber objects. However the problem arises when looking at more complex parts of the response: nested arrays/dictionaries.
The only solution I've found is to hand-code the finding and converting of all of these properties which I feel is a really poor practice. Here's an excerpt of what I'm doing:
NSDictionary *skillsDictionary = json[#"skills"];
if ([skillsDictionary isKindOfClass:[NSDictionary class]]) {
NSArray *activeArray = skillsDictionary[#"active"];
NSArray *passiveArray = skillsDictionary[#"passive"];
NSMutableArray *mutActives = [NSMutableArray array];
NSMutableArray *mutPassives = [NSMutableArray array];
if ([activeArray isKindOfClass:[NSArray class]]) {
[activeArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *activeJSON = (NSDictionary*)obj;
D3Skill *skill = [D3Skill activeSkillFromJSON:activeJSON];
if (skill) {
[mutActives addObject:skill];
}
}
}];
}
if ([passiveArray isKindOfClass:[NSArray class]]) {
[passiveArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *passiveJSON = (NSDictionary*)obj;
D3Skill *skill = [D3Skill passiveSkillFromJSON:passiveJSON];
if (skill) {
[mutPassives addObject:skill];
}
}
}];
}
self.activeSkills = mutActives;
self.passiveSkills = mutPassives;
}
I use the SBJSON library and ASIHttpRequest to fetch and consume JSONs from a webservice of my own design, if I understand your question correctly, you'd just need to do something like this:
NSString *responseJSONasString = [fetchRequest responseString];
NSDictionary *itemResponseArray = [responseJSONasString JSONValue];
The SBJSON library will do the converting for you into NSObjects and put them into Arrays/Dictionaries using key:value coding on the JSON elements, so with the JSON you provided I could get the first hero's name with:
NSArray *heroes = [itemResponseArray objectForKey:#"heroes"];
NSDictionary *firstHero = [heroes objectAtIndex:0];
NSString *heroName = [firstHero objectForKey:"name"];
SBJson can be found here: http://stig.github.com/json-framework/
BWJSONMatcher is a lightweight library i used in my projects to convert JSON data from RESTful api to my business data model. And it goes like this:
...
NSString *jsonString = #"{your-json-string}";
YourValueObject *dataModel = [YourValueObject fromJSONString:jsonString];
NSDictionary *jsonObject = #{your-json-object};
YourValueObject *dataModel = [YourValueObject fromJSONObject:jsonObject];
...
YourValueObject *dataModel = instance-of-your-value-object;
NSString *jsonString = [dataModel toJSONString];
NSDictionary *jsonObject = [dataModel toJSONObject];
...
Related
I am working on a project where the facebook's friend list have to de displayed. I did all necessary coding to get the reponse , but the reponse is like the following
{"data":[{"name":"Ramprasad Santhanam","id":"586416887"},{"name":"Karthik Bhupathy","id":"596843887"},{"name":"Anyembe Chris","id":"647842280"},{"name":"Giri Prasath","id":"647904394"},{"name":"Sadeeshkumar Sengottaiyan","id":"648524395"},{"name":"Thirunavukkarasu Sadaiyappan","id":"648549825"},{"name":"Jeethendra Kumar","id":"650004234"},{"name":"Chandra Sekhar","id":"652259595"}
Can anyone please tell me how to save name and id in two different arrays.
Any help will be appreciated.
you can see below how html response parse . there i am getting facebook friends.
- (void)fbGraphCallback:(id)sender
{
if ( (fbGraph.accessToken == nil) || ([fbGraph.accessToken length] == 0) )
{
//restart the authentication process.....
[fbGraph authenticateUserWithCallbackObject:self andSelector:#selector(fbGraphCallback:)
andExtendedPermissions:#"user_photos,user_videos,publish_stream,offline_access,user_checkins,friends_checkins"];
}
else
{
NSLog(#"------------>CONGRATULATIONS<------------, You're logged into Facebook... Your oAuth token is: %#", fbGraph.accessToken);
FbGraphResponse *fb_graph_response = [fbGraph doGraphGet:#"me/friends" withGetVars:nil];// me/feed
//parse our json
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary * facebook_response = [parser objectWithString:fb_graph_response.htmlResponse error:nil];
//init array
NSMutableArray * feed = (NSMutableArray *) [facebook_response objectForKey:#"data"];
// NSMutableArray *recentFriends = [[NSMutableArray alloc] init];
arr=[[NSMutableArray alloc]init];
//adding values to array
for (NSDictionary *d in feed)
{
[arr addObject:d];
}
//NSLog(#"array is %# ",arr);
[fbSpinner stopAnimating];
[fbSpinner removeFromSuperview];
[myTableView reloadData];
}
}
This is json response you are getting. So you need a JSON parser to convert this string into Objective-C objects. In iOS App, you can use a library like the json-framework. This library will allow you to easily parse JSON and generate json from dictionaries / arrays (that's really all JSON is composed of).
From SBJson docs: After JSON parsing you will get this conversion
JSON is mapped to Objective-C types in the following way:
null -> NSNull
string -> NSString
array -> NSMutableArray
object -> NSMutableDictionary
true -> NSNumber's -numberWithBool:YES
false -> NSNumber's -numberWithBool:NO
integer up to 19 digits -> NSNumber's -numberWithLongLong:
all other numbers -> NSDecimalNumber
That looks like JSON, not HTML. (You probably already knew this, since you tagged the question with json I see.)
I'm not really sure why others are recommending third-party libraries to do this, unless you need to support rather old OS releases. Just use Apple's built-in NSJSONSerialization
class.
This is not HTML. This is JSON. You'll need a JSON parser for this.
A JSON parser would typically make an NSDictionary or NSArray out of the string. With my implementation, you'd do something like this:
NSMutableArray *names = [NSMutableArray array];
NSMutableArray *ids = [NSMutableArray array];
NSDictionary *root = [responseString parseJson];
NSArray *data = [root objectForKey:#"data"];
for (NSDictionary *pair in data)
{
[names addObject:[pair objectForKey:#"name"]];
[ids addObject:[pair objectForKey/#"id"]];
}
Recent versions of iOS contain a new Foundation class, NSJSONSerialization, that will handle any JSON parsing and serialization for you.
I used to have trouble parsing json, before I found a really good tutorial which I could follow. When I have been building my own webservices to parse json from I have followed the same steps, but now when I'm trying to parse twitters json-feed I'm drawing blanks. Here's my code.
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* itemNumber = [json objectForKey:#"posts"]; //2
NSUInteger numObjects = [itemNumber count];
arrayTweets = [[NSMutableArray alloc] init];
arrayTimes = [[NSMutableArray alloc] init];
if (numObjects != 0) {
int i = 0;
do
{
NSDictionary* loan = [itemNumber objectAtIndex:i];
NSString* text = [(NSDictionary*)loan objectForKey:#"text"];
[arrayTweets addObject:text];
NSString *time = [(NSDictionary*)loan objectForKey:#"created_at"];
[arrayTweets addObject:time];
i++;
} while (i < numObjects);
} else {
NSLog(#"Nej");
}
[tableTweet performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
Notice the "NSArray* itemNumber = [json objectForKey:#"posts"];". Since my old json-feed used to look something like this...
{"status":"ok","count":4,"count_total":4,"pages":1,"posts":[{"id":58,"type":
it was working since I had the "posts" before :[, but now with the twitter feed it just starts right into:
[{"created_at":"Mon Aug 06 19:16:42 +0000 2012","id":232555817835048961,"...
And I have no idea what to do. I realize this is stupid, but I don't think I'm going to learn unless someone explains it to me.
Any help appreciated!
Aah, there is a small problem with your code. The root object of your previous feed used to be a dictionary. You can see this by the { sign at the beginning. Your new feed instantly gives you an array back [. So you don't have to parse your data as a dictionary but as as NSArray. Without further ado, explanation here: https://stackoverflow.com/a/8356919/341358
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.
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"];
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!