I'm trying to parse some JSON in iOS to get a URL from google. I've gotten pretty far (see code below), the problem is since I've never worked with JSON before, I'm not really sure how things are defined. How can I look for an object that's inside another object?
For example if I want to get the first unescapedUrl here how can I do that? I tried simply using objectForKey, but it didn't work.. I'm guessing I'm going to have to specify the exact path. How can I do that?
Here's the code I'm using:
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
#define kLatestSearchURL [NSURL URLWithString: #"https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=monkey"] //2
#import "ViewController.h"
#interface NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress;
-(NSData*)toJSON;
#end
#implementation NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress
{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString: urlAddress] ];
__autoreleasing NSError* error = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
-(NSData*)toJSON
{
NSError* error = nil;
id result = [NSJSONSerialization dataWithJSONObject:self options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: kLatestSearchURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
// ---------------------------- Will Start Parsing Here ------------------------------
}
#end
God save literals (Xcode 4.5 and iOS 6.0 or later SDK, LLVM Compiler 4.0 required). Deploys back to iOS 5.
NSString *url = resp[#"responseData"][#"results"][0][#"unescapedUrl"];
You can use objectForKey: for dictionaries and objectAtIndex: for arrays:
NSString *url = [[[[resp objectForKey:#"responseData"]
objectForKey:#"results"]
objectAtIndex:0]
objectForKey:#"unescapedUrl"];
Related
I have built a translate application in ios. The application uses the Yandex translation api. I followed this tutorial: http://www.raywenderlich.com/5492/working-with-json-in-ios-5
My ViewController.m looks like this (I took out my api key):
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
#import "ViewController.h"
#end
#interface NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress;
-(NSData*)toJSON;
#end
#implementation NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress
{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString: urlAddress] ];
__autoreleasing NSError* error = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
-(NSData*)toJSON
{
NSError* error = nil;
id result = [NSJSONSerialization dataWithJSONObject:self options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
// NSData* data = [[NSData dataWithContentsOfURL: TranslateText] ];
NSData*data = [NSURL URLWithString: [NSString stringWithFormat: #"https://translate.yandex.net/api/v1.5/tr.json/translate?key=apikeys&lang=en-es&text=%#", textfield.text]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* TranslatedText = [json objectForKey:#"text"]; //2
NSLog(#"Text that was translated: %#", TranslatedText); //3
// 1) Get the latest loan
//NSDictionary* ttext = [TranslatedText objectAtIndex:0];
NSString* ttext = [TranslatedText objectAtIndex:0];
// 3) Set the label appropriately
humanReadble.text = [NSString stringWithFormat:#"%#",
//[ttext objectForKey:#"name"],
ttext];
}
#end`
When I run the app, I get the error Thread 1: signal SIGABRT on this line of code:
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
What should I do?
The error in your code is the use of the colon there. You should have the line be...
[NSURL URLWithString: [NSString stringWithFormat: #"https://translate.yandex.net/api/v1.5/tr.json/translate?apikeyes&text=%#", textfield.text];
Also, I do not know why you would do a #define. Grab the information in the method for handling the button getting pressed.
NSURL * translateURL = [NSURL URLWithString: [NSString stringWithFormat: #"https://translate.yandex.net/api/v1.5/tr.json/translate?apikeyes&text=%#", textfield.text];
Ok, this is my first approach to JSONs in Objective-C (and i'm quite new to the last one too). i'm to get infos stored my json to use them in Objective-C, but when trying to load it i get null in response from NSLog(#"%#",allData); on. Can anybody please tell me what i am doing wrong? thanks in advance for your time and your patience.
oh and if needed here's the json:
http://jsonviewer.stack.hu/#http://conqui.it/ricette.json
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"recipes" ofType:#"json"];
NSError *error = nil;
NSMutableData *JSONData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];
NSLog(#"%#",JSONData);
NSArray *allData = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:nil];
NSLog(#"%#",allData);
for (NSDictionary *diction in allData) {
NSString *recipe = [diction objectForKey:#"recipe"];
[array addObject:recipe];
}
NSLog(#"%#",array);
The JSONObjectWithData method has an error parameter, of which you can avail yourself in order to diagnose the problem. For example:
NSError *error = nil;
NSArray *allData = [NSJSONSerialization JSONObjectWithData:JSONData
options:0
error:&error];
if (error)
NSLog(#"%s: JSONObjectWithData error: %#", __FUNCTION__, error);
In your comments, you suggest that you received an error about "Unescaped control character around character 414." That would suggest an error in the JSON, itself, which you might want to validate by copying into http://jsonlint.com/ and see if it reports any issues.
In response to the broader question about whether there are any Objective-C issues, there are no coding errors, per se. I can't comment on the for loop which clearly assumes that allData is an array of dictionaries to which I cannot attest without seeing the JSON. But I'll take your word for it. But, yes, the Objective-C code looks fine (albeit, a little light on checking of the return values types and error objects).
For example, if you wanted some diagnostic assert statements that you could use during development, you might do something like:
NSArray *allData = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:nil];
NSAssert(error, #"%s: JSONObjectWithData error: %#", __FUNCTION__, error);
NSLog(#"%s: array=%#", __FUNCTION__, array);
NSAssert([allData isKindOfClass:[NSArray class]], #"allData is not an array");
for (NSDictionary *diction in allData) {
NSAssert([diction isKindOfClass:[NSDictionary class]], #"%s: diction is not a dictionary (%#), __FUNCTION__, diction);
NSString *recipe = [diction objectForKey:#"recipe"];
NSAssert(recipe, #"%s: Did not find recipe key in diction (%#)", __FUNCTION__, diction);
[array addObject:recipe];
}
If any of these errors were possible runtime errors in production, you'd replace assert statements with if statements that do the necessary error handling. But hopefully it illustrates the concept.
Problem in your response is that , string values are unable to concatenate.So, I have to manually remove those tabs and new lines.
At five places you are getting error i.e:
pelate.
pasta.
pomodoro.
saporiti).
\t
- (void)viewDidLoad
{
NSString *urlStr=[NSString stringWithFormat:#"http://www.conqui.it/ricette.json"];
urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *fileNameURL=[NSURL URLWithString:urlStr];
NSLog(#"url is %#",urlStr);
NSMutableURLRequest *filenameReq=[[NSMutableURLRequest alloc] initWithURL:fileNameURL];
NSData *responseData=[NSURLConnection sendSynchronousRequest:filenameReq returningResponse:nil error:nil];
NSString *responseString=[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
responseString=[[responseString componentsSeparatedByString:#"\n"] componentsJoinedByString:#""];
responseString=[responseString stringByReplacingOccurrencesOfString:#"\t" withString:#""];
responseData=[responseString dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"response String is %#",responseString);
[NSCharacterSet characterSetWithCharactersInString:responseString];
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:responseData options: 0 error: &e];
NSLog(#"JSON Array is %# & error is %#",jsonArray,e);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
Since the twitter api is changed my app no longer posts tweet. Every thing was working fine before and according to new API only request pattern should change ? All the other stuff initialising the engine and rest should be same ?
And the method for sharing should be modified, so I modified it but getting "Bad Authentication 215". All the token info and other things I got it from the authentication header generated from twitter itself:
- (void) shareOnTwitter
{
NSString *urlString = [NSString stringWithFormat:#"https://api.twitter.com/1.1/statuses/update.json"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:#"check Check CHECK" forKey:#"status"];
[dict setValue:#"-- - - - - -" forKey:#"oauth_consumer_key"];
[dict setValue:#"- - - - - - -" forKey:#"oauth_nonce"];
[dict setValue:#"- - - - - - -" forKey:#"oauth_signature"];
[dict setValue:#"HMAC-SHA1" forKey:#"oauth_signature_method"];
[dict setValue:#"- - - - - - -" forKey:#"oauth_timestamp"];
[dict setValue:#"- - - - - - -" forKey:#"oauth_token"];
[dict setValue:#"1.0" forKey:#"oauth_version"];
NSString *jsonString = [dict JSONRepresentation];
NSData *jsonData = [NSData dataWithBytes:[jsonString UTF8String] length:jsonString.length];
[request setHTTPBody:jsonData];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSLog(#"My request...%#", jsonString);
NSData *urlData;
NSURLResponse *response1;
NSError *error = nil;
urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response1 error:&error];
if(error)
{
NSLog(#"Error %#",error);
}
if(!urlData)
{
NSLog(#"No connection!");
}
NSString *responseStr = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#" Twitter : ... %#", responseStr);
}
Try below code. Its worked for me :
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
{
if (granted)
{
NSArray *accounts = [accountStore accountsWithAccountType:accountType];
if (accounts.count)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:#"check Check CHECK" forKey:#"status"];
NSString *retweetString = [NSString stringWithFormat:#"https://api.twitter.com/1.1/statuses/update.json"];
NSURL *retweetURL = [NSURL URLWithString:retweetString];
TWRequest *request = [[TWRequest alloc] initWithURL:retweetURL parameters:dict requestMethod:TWRequestMethodPOST];
request.account = [accounts objectAtIndex:0];
[request performRequestWithHandler:^(NSData *responseData1, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (responseData1)
{
NSError *error1 = nil;
id response = [NSJSONSerialization JSONObjectWithData:responseData1 options:NSJSONReadingMutableLeaves error:&error1];
}
}];
}
}
}];
Twitter explained every thing in its new documentation from Authentication to loads of trivial things but no easy way for previous apps using 1.0 api, how to post a tweet or to upgrade.
Few changes to make it work (works for OAuth):
Open the MGTwitterEngine.m >
Make sure the macros are defined like this:
#define TWITTER_DOMAIN #"api.twitter.com/1.1"
#define HTTP_POST_METHOD #"POST"
#define TWITTER_SEARCH_DOMAIN #"search.twitter.com"
#define DEFAULT_CLIENT_VERSION #"1.0"
#if YAJL_AVAILABLE
#define API_FORMAT #"xml"
#import "MGTwitterStatusesYAJLParser.h"
#import "MGTwitterMessagesYAJLParser.h"
#import "MGTwitterUsersYAJLParser.h"
#import "MGTwitterMiscYAJLParser.h"
#import "MGTwitterSearchYAJLParser.h"
#else
#define API_FORMAT #"json"
#if USE_LIBXML
#import "MGTwitterStatusesLibXMLParser.h"
#import "MGTwitterMessagesLibXMLParser.h"
#import "MGTwitterUsersLibXMLParser.h"
#import "MGTwitterMiscLibXMLParser.h"
#else
#import "MGTwitterStatusesParser.h"
#import "MGTwitterUsersParser.h"
#import "MGTwitterMessagesParser.h"
#import "MGTwitterMiscParser.h"
#endif
#endif
You will be surprised that - (void) requestSucceeded: (NSString *) requestIdentifier and - (void) requestFailed: (NSString *) requestIdentifier withError: (NSError *) error will be called. But it is pretty easy to put a check that if succeeded is called than don't do anything in failed method.
My code below was working, before, but suddenly it stopped working. Could anyone tell me what I'm doing wrong?
Output of NSLog():
2013-05-14 16:06:35.475 Run.PH[1502:19a03] background = (null)
2013-05-14 16:06:35.476 Run.PH[1502:19a03] logo = (null)
2013-05-14 16:06:35.476 Run.PH[1502:19a03] json = (null)
2013-05-14 16:06:35.476 Run.PH[1502:19a03] welcome = (null)
Codes:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *urlString = [NSString stringWithFormat:#"http://run.ph/json/test_json_dyna.jsp"];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
if(data != nil)
{
NSError *error;
_json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
_welcomeMessage.text = [[_json objectForKey:#"welcome"] objectForKey:#"message"];
NSString *background = [[_json objectForKey:#"welcome"] objectForKey:#"background"];
NSString *logo = [[_json objectForKey:#"welcome"] objectForKey:#"logo"];
NSLog(#"background = %#", background);
NSLog(#"logo = %#", logo);
NSLog(#"json = %#", _json);
NSLog(#"welcome = %#", _welcomeMessage.text);
NSURL *urlBackground = [NSURL URLWithString:background];
NSData *dataBackground = [NSData dataWithContentsOfURL:urlBackground];
[_background setImage:[UIImage imageWithData:dataBackground]];
NSURL *urlLogo = [NSURL URLWithString:logo];
NSData *dataLogo = [NSData dataWithContentsOfURL:urlLogo];
[_logo setImage:[UIImage imageWithData:dataLogo]];
}
else
{
NSLog(#"Error!");
}
[self.view reloadInputViews];
}
Just NSLog your URL response you will get wats wrong with your json.
Try this
NSString *respo = [NSString stringWithContentsOfURL:[NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
NSLog(#"Response - %#",respo);
It has return "\r" and newline character "\n" with your response.
Check this
EDIT
Now your URL is working fine, problem was with your web service end.
I am currently working on an iPhone application that takes in data from the following source:
I am trying to figure out how to parse it into a human readable format in say a text field.
My code so far is:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *urlString = [NSString stringWithFormat:#"http://dev.threesixtyapp.com/api/events.php?action=available&id=1"];
NSURL *url =[NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"%#",json);
}
http://stig.github.com/json-framework/ - SBJson is a great framework for encoding/decoding JSON. I recommend you check it out...It will parse it for you into an NSDictionary, and you simply set the text of the textfield equal to the value in the NSDictionary that you want. It's pretty straightforward using this framework. Your Json should just be a string when you pass it to the SBJson functions btw
First of all you have to understand the data structure of your json.
You can use JSON Viewer to view the data structure of your json.
As I can see you are getting array of objects consisting of event_title, date_from and date_to.
NSError *error = nil;
NSArray *jsonArry = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"%#",jsonArry);
for (NSDictionary *dict in jsonArry) {
NSString * title = [dict objectForKey:#"event_title"];
NSString * dateTo = [dict objectForKey:#"date_to"];
NSString * dateFrom = [dict objectForKey:#"date_from"];
NSLog(#"title=%#,dateTo=%#,dateFrom=%#",title,dateTo,dateFrom);
}