I am retrieving JSON information for an API and it says on the API that it is in JSON but I noticed it is in JSONP or "json with padding" as some call it. I tired to look everywhere to find how to parse this but no luck. The information I am trying to receive is this:
({"book":[{"book_name":"James","book_nr":"59","chapter_nr":"3","chapter":
{"16":{"verse_nr":"16","verse":"For where envying and strife is, there is confusion and
every evil work."}}}],"direction":"LTR","type":"verse"});
The link to the data is https://getbible.net/json?p=James3:16, so you can look at it directly.
This is the code I am using to try to retrieve the JSON Data and parse it into a NSMutableDictionary.
-(void)fetchJson {
NSString *currentURL = [NSString stringWithFormat:#"https://getbible.net/json?p=James"];
NSURL *url = [NSURL URLWithString:currentURL];
NSData *data = [[NSData alloc]initWithContentsOfURL:url];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
NSMutableData *receivedData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
[receivedData setLength:0];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url MIMEType:#".json" expectedContentLength:-1 textEncodingName:nil];
expectedTotalSize = [response expectedContentLength];
if ([data length] !=0) {
NSLog(#"appendingData");
[receivedData appendData:data];
if(connection){
NSLog(#"Succeeded! Received %lu bytes of data",(unsigned long)[receivedData length]);
}
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if(jsonResponse){
NSArray *responseArr = [jsonResponse mutableCopy];
NSLog(#"%lu",(unsigned long)[responseArr count]);
}else if (!jsonResponse){
//do internet connection error response
}
}
}
The results I am getting back from putting a breakpoint in the code is:
jsonResponse returns NULL
NSError NSCocoaErrorDomain code - 3840
but my NSData *data is returning 15640 bytes.
My console is displaying this from the NSLogs I used for debugging:
2014-04-20 01:27:31.877 appendingData
2014-04-20 01:27:31.879 Succeeded! Received 15640 bytes of data
I am receiving the data correctly but I am not parsing it correctly I know the error is because the JSON is in JSONP format. If anyone could please help with this I would appreciate it so much. I have tired to give as much detail on this question as I can but if you need more information just let me know so I can add it and make this as clear as possible.
Your code has at least two separate attempts to download the data. Neither is really correct. The code also only works with JSON, not JSONP.
Try this:
NSURL *url = [NSURL URLWithString:#"https://getbible.net/json?p=James"];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data) {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSRange range = [jsonString rangeOfString:#"("];
range.location++;
range.length = [jsonString length] - range.location - 2; // removes parens and trailing semicolon
jsonString = [jsonString substringWithRange:range];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *jsonError = nil;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&jsonError];
if (jsonResponse) {
// process jsonResponse as needed
} else {
NSLog(#"Unable to parse JSON data: %#", jsonError);
}
} else {
NSLog(#"Error loading data: %#", error);
}
}];
One problem is that the data you're downloading has extraneous information at the beginning and end. The JSON being delivered by your URL is:
({"book":[{"book_name":"James","book_nr":"59","chapter_nr":"3","chapter":{"16":{"verse_nr":"16","verse":"For where envying and strife is, there is confusion and every evil work."}}}],"direction":"LTR","type":"verse"});
As the error message you're seeing indicates: you need to remove the initial ( from the beginning of the string and the ); from the end so that your JSON will start with the dictionary that your code expects. You can do this by calling subdataWithRange: on your NSData object:
NSData* jsonData = [data subdataWithRange:NSMakeRange(1, data.length-3)];
NSDictionary* jsonResponse = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:&error];
Just to update everyone, the NSURLRequest has been deprecated in iOS9. I tried the answer by #rmaddy, and I didn't receive anything either (just like what #lostAtSeaJoshua was encountering I guess). I have updated rmaddy's answer to reflect the NSURLSession implementation that has (I think) replaced NSURLRequest:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:#"http://somerandomwebsite.com/get.php?anotherRandomParameter=5"];
[[session dataTaskWithURL:url
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
if (data) {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"stringJSONed: %#",jsonString);
//Do something with the received jsonString, just like in # rmaddy's reply
} else {
NSLog(#"Error loading data: %#", error);
}
}] resume];
Just a heads up notice, when I first ran it, it gave me the security error. What you need to do (if you are using http) is to add this to your plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
I have to mention that after the NSAllowArbitraryLoads key, there are most probably other keys and values, such as NSExceptionDomain. But they're not really relevant to this answer I think. If you need to look further, let me know and I will dig deeper :)
Related
I'm following the documentation here.
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
I have a receipt-data from my team that I'm trying to validate, they're getting error code 21002 which was malformed JSON. It looks like they had extra parameters appended to the base64 data, so I tried removing those and sending:
- (void)viewDidLoad {
[super viewDidLoad];
NSData *receipt; // Sent to the server by the device
// Create the JSON object that describes the request
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data": #"<<$mybase64data>>", #"password" : #"<<$thepassword>>"};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];
if (!requestData) { /* ... Handle error ... */ }
// Create a POST request with the receipt data.
NSURL *storeURL = [NSURL URLWithString:#"https://buy.itunes.apple.com/verifyReceipt"];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:#"POST"];
[storeRequest setHTTPBody:requestData];
// Make a connection to the iTunes Store on a background queue.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
/* ... Handle error ... */
NSLog(#"conerror %#", connectionError);
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(#"hello %#", jsonResponse);
NSLog(#"error %#", error);
if (!jsonResponse) {
}
}
}];
}
result:
2017-03-03 22:45:47.454 receipttest[89851:352604] hello {
exception = "com.apple.jingle.mzfairplay.validators.DrmInvalidArgumentException";
status = 21002;
}
2017-03-03 22:45:47.455 receipttest[89851:352604] error (null)
Something to keep in mind: In this example the data is sent to Apple straight from the App, but you might do this from a server too. When you testing your server App don't use NSLog() to print your base64 data, NSLog truncates data.
I had this issue when using a receipt from a test user that was a couple days old with a yearly auto-renewable subscription.
I checked the above helpful responses about extra characters etc (I also checked I had supplied my app secret for auto-renewable) with no joy.
In the end I tried creating A NEW SANDBOX user and it worked first time
with no other changes other than the new Receipt!
Hope this helps someone.
I also received the same error response for serveral receipts. The solution for me was to remove all occurrences of \r\n.
Maybe you have the same issue. I still haven't figured out when and why these chars are inserted.
Pay attention to URLENCODE:
- (NSString *)URLEncodedString
{
// CharactersToBeEscaped = #":/?&=;+!##$()~',*";
// CharactersToLeaveUnescaped = #"[].";
NSString *unencodedString = self;
NSString *encodedString = (NSString *)
CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8));
return encodedString;
}
I have have some trouble in understanding what is needed to fetch a JSON file with mantle.h from a URL.
Can someone give me an example of how it works?
For example:
-I have a URL www.example.com with a JSONFile as follows:
{
"name": "michael"
}
How could I fetch it?
I use this process for fetching JSON:
NSURL *s = url;//Put your desird url here
NSURLRequest *requestURL = [NSURLRequest requestWithURL:s cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.00];
NSHTTPURLResponse *response;
NSError *error = [[NSError alloc]init];
NSData *apiData = [NSURLConnection sendSynchronousRequest:requestURL returningResponse:&response error:&error];
dictionaryData = [NSJSONSerialization JSONObjectWithData:apiData options:kNilOptions error:&error];
Now the dictionaryData contains your JSON. You can fetch it by:
NSString *name = [dictionaryData valueForKey:#"name"];
And make sure you are making async request. For this put the code within this block:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//Put the code here
});
Hope this helps.. :)
Call it with following method
[super getRequestDataWithURL:urlString
andRequestName:sometext];
You will get response in the following method if successful
- (void)successWithRequest:(AFHTTPRequestOperation *)operation withRespose:(id)responseObject withRequestName:(NSString *)requestName {
NSString *response = operation.responseString;
id jsonObject = [response objectFromJSONString];
if(![super checkforServerRequestFailureErrorMessage:jsonObject]) {
[self.leaderboardProxyDelegate leaderboardListSuccessful:jsonObject];
}
}
You will get dictionary in jsonObject
I have the following problem. I have a Model, called User. When the user now logins with Facebook, my app checks if the user exists already in the database. To not freeze the UI (since I'm coming from Android) I thought to use NSURLConnection sendAsynchronousRequest. What worked at first was the following:
My User Model had a method to do the whole task of the AsynchronousRequest and then when finished would set a variable to loading. Then other classes, could simply check with
while ( !user.loading ) if the Request was finished or not. The problem that came here to me, was, that now, I had to put this method in every Model. So instead of this, I created a new Class HTTPPost. This class now has the method that gets an NSDictionary passed and returns one. This works ALMOST. The problem I was now encountering is, that I couldn't really determine if the process was finished or not. So I started to create a new class called Globals and use global Variable loading. But the global variable is ALWAYS NO. So, what would be the best way to do this?
Here is my code:
This is where I check for the user and load it. resultDictionary is the NSDictionary where everything gets loaded in, but is always nil
[user loadModelFrom:[NSString stringWithFormat:#"WHERE facebookId='%#'", graphUser.id]];
NSLog(#"%#", user.resultDictionary);
if ( user.resultDictionary == nil ) {
NSLog(#"NIL");
} else {
NSLog(#"NOT NIL");
}
The problem now, is, that, since I'm sending an AsynchronousRequest, the resultDictionary is always nil. What I did before and worked was the following.
In my Model I had the HTTP Request and a variable named loading. Now I set loading to false until the response has been made into a NSDictionary
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
But, then I had another problem. I had to do this in all my Models again... So I created a new Class that subclasses NSObject, that has the asynchronousRequest. This is the whole request
-(NSDictionary *)doHttpRequest:(NSDictionary *)postDict{
loading = NO;
__block NSDictionary *returnDict;
NSError *error;
NSString *jsonString;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postDict
options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
error:&error];
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSURL *aUrl = [NSURL URLWithString:#"http://xx.xx-xx.xx/xx.xx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", #"xx", #"xx"];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedString]];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
}];
[queue waitUntilAllOperationsAreFinished];
loading = YES;
return returnDict;
}
As you can see I have now a variable called loading. It is a global variable. But somehow, the variable is always NO.
What would be the best way to do this? I hope I'm understandable, I'm new to Objective-C, and English isn't my native language.
UPDATE
I modified the code to look like a user provided here, but still not working!
HTTPPost.h
-(void)doHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
__block NSDictionary *returnDict;
NSError *error;
NSString *jsonString;
NSString *authValue;
NSString *authStr;
NSData *jsonData;
NSData *authData;
NSURL *aUrl;
NSMutableURLRequest *request;
NSOperationQueue *queue;
jsonData = [NSJSONSerialization dataWithJSONObject:postDict
options:NSJSONWritingPrettyPrinted
error:&error];
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
aUrl = [NSURL URLWithString:#"http://xx.xx-xx.com/xx.php"];
request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
queue = [[NSOperationQueue alloc] init];
authStr = [NSString stringWithFormat:#"%#:%#", #"xx", #"xx"];
authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedString]];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
if ( completion ) {
completion(returnDict, error);
}
}];
}
//User.h
[_httpPost doHttpRequest:_dbDictionary completion:^(NSDictionary *dict, NSError *error) {
NSLog(#"completed") // NEVER GETS FIRED
}];
It seems that you're trying to take an asynchronous process (sendAsynchronousRequest) , and make it behave like a synchronous process (i.e. you appear to want to wait for it). You should not do that. You should to embrace the asynchronous patterns rather than fighting them.
The sendAsynchronousRequest method has a completion block that specifies what you want to do when the request is done. Do not try to put the code after the block and (try to) wait for the block to complete, but rather put any of your code that is dependent upon the completion of the network request inside the completion block, or have the completion block call your code.
A common way would be to give your own methods their own completion blocks and then call those blocks in the completionHandler of sendAsynchronousRequest, something like:
- (void)performHttpRequest:(NSDictionary *)postDict completion:(void (^)(NSDictionary *dictionary, NSError *error))completion
{
// prepare the request
// now issue the request
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
if (completion)
completion(data, error);
} else {
NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData:data
options: NSJSONReadingMutableContainers
error: &error];
if (completion)
completion(returnDict, error);
}];
}
Now, when you want to perform your request, you simply do:
[self performHttpRequest:someDictionary completion:^(NSDictionary *dictionary, NSError *error) {
if (error) {
// ok, handle the error here
} else {
// ok, use the `dictionary` results as you see fit here
}
];
Note, the method that calls this performHttpRequest (let's imagine you called it from loadModelFrom ) now behaves asynchronously, itself. So you might want to employ this completion-block pattern again, e.g. adding your own completion block parameter to loadModelFrom, and then invoke that block in the completion handler loadModelFrom passes to performHttpRequest.
But hopefully you get the idea: Never try to wait for a completion block, but rather just put inside that block anything you want it to do when its done. Whether you use AFNetworking (which I'd advise), or continue to use sendAsynchronousRequest, this is a very useful pattern with which you should become familiar.
Update:
The revised code sample (largely) works great for me. Seeing your revised question, a couple of observations:
I am not familiar with this base64EncodedString method. In iOS 7, there is the native base64EncodedStringWithOptions method (or for earlier iOS versions use base64Encoding). Or are you using a third party base-64 NSData category?
There's no point in creating jsonString, only to then convert it back to a NSData. Just use jsonData in your request.
The same is true with responseBody: Why convert to string only to convert back to NSData?
There's no point in having returnDict to be defined as __block outside the sendAsynchronousRequest block. Just define it inside that block and the __block qualifier is then no longer necessary.
Why create a NSOperationQueue for the completionHandler of sendAsynchronousRequest? Unless I'm doing something really slow that merits running on a background queue, I just use [NSOperationQueue mainQueue], because you invariably want to update the app's model or UI (or both), and you want to do that sort of stuff on the main queue.
The request still runs asynchronously but the queue parameter just specifies which queue the completion block will run on.
By the way, in sendAsynchronousRequest, you aren't checking to see if the request succeeded before proceeding with JSONObjectWithData. If the request failed, you could theoretically be losing the NSError object that it returned. You really should check to make sure the request succeeded before you try to parse it.
Likewise, when you originally dataWithJSONObject the parameters in postDict, you really should check for success, and if not, report the error and quit.
I notice that you're using the NSJSONReadingMutableContainers option. If you really need a mutable response, I'd suggest making that explicit in your block parameters (replacing all the NSDictionary references with NSMutableDictionary). I assume you don't really need it to be mutable, so I therefore recommend removing the NSJSONReadingMutableContainers option.
Likewise, when creating the JSON, you don't need to use the NSJSONWritingPrettyPrinted option. It only makes the request unnecessary larger.
Combining all of this, that yields:
-(void)performHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
NSError *error;
NSString *authValue;
NSString *authStr;
NSData *jsonData;
NSData *authData;
NSURL *aUrl;
NSMutableURLRequest *request;
jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:0 error:&error];
if (!jsonData) {
if (completion)
completion(nil, error);
return;
}
aUrl = [NSURL URLWithString:#"...."];
request = [NSMutableURLRequest requestWithURL:aUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
authStr = [NSString stringWithFormat:#"%#:%#", #"xx", #"xx"];
authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
if ([authData respondsToSelector:#selector(base64EncodedStringWithOptions:)])
authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedStringWithOptions:0]];
else
authValue = [NSString stringWithFormat:#"Basic %#", [authData base64Encoding]]; // if only supporting iOS7+, you don't need this if-else logic and you can just use base64EncodedStringWithOptions
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (!data) {
if (completion)
completion(nil, error);
return;
}
NSError *parseError = nil;
NSDictionary *returnDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (completion) {
completion(returnDict, parseError);
}
}];
}
And if this is being called from another method that needs to handle the fact that this is happening asynchronously, then it would employ a completion block pattern, too:
- (void)authenticateUser:(NSString *)userid password:(NSString *)password completion:(void (^)(BOOL success))completion
{
NSDictionary *dictionary = #{ ... };
[self performHttpRequest:dictionary completion:^(NSDictionary *dict, NSError *error) {
if (error) {
completion(NO);
return;
}
// now validate login by examining resulting dictionary
BOOL success = ...;
// and call this level's completion block
completion(success);
}];
}
Then the view controller might access that method with something like:
// maybe add UIActivityIndicatorView here
[self.userModel authenticateUser:self.userTextField.text password:self.passwordTextField.text completion:^(BOOL success) {
// remove UIActivityIndicatorView here
if (success) {
// do whatever you want if everything was successful, maybe segue to another view controller
} else {
// show the user an alert view, letting them know that authentication failed and let them try again
}
}];
After seeing you adding specific code to handle request and its responses, I would point out that you should try using AFNetworking. It abstracts out lots of boiler plate code.
As you mentioned, you are new to obj-c, it may take some time to understand AFNetworking but in long run, it will save you lots of headache. Plus it is one of the widely used open source for network related stuff.
I hope this would be helpful.
If you want to wait for a request, then you should not use sendAsynchronousRequest.
Use sendSynchonousRequest instead. That's where it's made for:
NSURLResponse *response;
NSError * error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
But, the UI is blocked when the synchronous call is made. I doubt if that is what you want.
am starting to build login form reading from external server via http request i need to parse json result to get user name
- (IBAction)getlogin:(UIButton *)sender {
NSString *rawStrusername = [NSString stringWithFormat:#"username=%#",_username.text];
NSString *rawStrpassword = [NSString stringWithFormat:#"password=%#",_password.text];
NSString *post = [NSString stringWithFormat:#"%#&%#", rawStrusername, rawStrpassword];
// NSString *post = #"rawStrusername&rawStrpassword";
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
/* NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]]; */
NSURL *url = [NSURL URLWithString:#"http://www.othaimmarkets.com/my_services_path/user/login.json"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
/* [request setValue:postLength forHTTPHeaderField:#"Content-Length"]; */
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSLog(#"responseData: %#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
//NSLog(#"responseData: %#", responseData);
}
I get this result:
{"sessid":"g2ev7til6d750ducrkege0cbj2","session_name":"SESS02795057fe9e6b2fc0777bf4057b248f","user":{"uid":"617","name":"mohammed.abdelrasoul#gmail.com","mail":"mohammed.abdelrasoul#gmail.com","mode":"0","sort":"0","threshold":"0","theme":"","signature":"","signature_format":"0","created":"1316602317","access":"1352643854","login":"1352666338","status":"1","timezone":"10800","language":"ar","picture":"","init":"mohammed.abdelrasoul#gmail.com","data":"a:5:{s:18:\"country_iso_code_2\";s:2:\"SA\";s:13:\"timezone_name\";s:11:\"Asia/Riyadh\";s:5:\"block\";a:1:{s:7:\"webform\";a:1:{s:15:\"client-block-88\";i:1;}}s:13:\"form_build_id\";s:37:\"form-3ae73833f08accc7abe5517347ea87eb\";s:7:\"contact\";i:0;}","country_iso_code_2":"SA","timezone_name":"Asia/Riyadh","block":{"webform":{"client-block-88":1}},"form_build_id":"form-3ae73833f08accc7abe5517347ea87eb","contact":0,"roles":{"2":"authenticated user"}}}
Or, formatted for the sake of legibility:
{
"sessid":"g2ev7til6d750ducrkege0cbj2",
"session_name":"SESS02795057fe9e6b2fc0777bf4057b248f",
"user":{
"uid":"617",
"name":"mohammed.abdelrasoul#gmail.com",
"mail":"mohammed.abdelrasoul#gmail.com",
"mode":"0",
"sort":"0",
"threshold":"0",
"theme":"",
"signature":"",
"signature_format":"0",
"created":"1316602317",
"access":"1352643854",
"login":"1352666338",
"status":"1",
"timezone":"10800",
"language":"ar",
"picture":"",
"init":"mohammed.abdelrasoul#gmail.com",
"data":"a:5:{s:18:\"country_iso_code_2\";s:2:\"SA\";s:13:\"timezone_name\";s:11:\"Asia/Riyadh\";s:5:\"block\";a:1:{s:7:\"webform\";a:1:{s:15:\"client-block-88\";i:1;}}s:13:\"form_build_id\";s:37:\"form-3ae73833f08accc7abe5517347ea87eb\";s:7:\"contact\";i:0;}",
"country_iso_code_2":"SA",
"timezone_name":"Asia/Riyadh",
"block":{
"webform":{
"client-block-88":1
}
},
"form_build_id":"form-3ae73833f08accc7abe5517347ea87eb",
"contact":0,
"roles":{
"2":"authenticated user"
}
}
}
how i can get the objects data or parse the result to get user name
any help or examples will be appreciated
You need to use the NSJSONSerialization class method, JSONObjectWithData:options:error: to create an NSDictionary:
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
if (! error) {
NSLog(#"%#",jsonDict);
}else{
NSLog(#"%#",error.localizedDescription);
}
This will get you to the point where you can look at the dictionary, which will be easier to read. It looks like you need to use objectForKey:#"sessid" to get you to user, then objectForKey#"user", then objectForKey:#"name" to get you to the name.
Check out this framework for parsing json. https://github.com/stig/json-framework/
Also check out this answer iPhone/iOS JSON parsing tutorial. You'll find a link to a tutorial you can do to get acquainted with json parsing in ios.
See this answer and some code :
NSMutableData *data; // Contains data received from the URL connection declares in header
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)dataIn
{
// Do it this way because connection doesn't guarantee all the data is in
POLLog(#" Tide View connection");
[data appendData:dataIn];
}
- (void) connectionDidFinishLoading:(NSURLConnection *) conn
{
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *results = [jsonString JSONValue]; // This is a new category added to the NSString by SBJSON
//100 parameters
for (int n=0;n<=100;n++)
{
// Get all the returned results
params[n] = [[results objectForKey:[NSString stringWithFormat:#"param%d",n]] floatValue];
}
To expand upon rdelmar's answer (which I think you should accept), you can use NSJSONSerialization and then navigate the NSDictionary results to extract the userName:
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:responseData
options:0
error:&error];
if (error == nil) {
NSDictionary *userDictionary = [jsonDict objectForKey:#"user"];
NSString *userName = [userDictionary objectForKey:#"name"];
// do what you need with the userName
} else {
NSLog(#"%#",error.localizedDescription);
}
Or if using the latest version of Xcode, you can replace those objectForKey references with the even more concise Modern Objective-C syntax:
NSDictionary *userDictionary = jsonDict[#"user"];
NSString *userName = userDictionary[#"name"];
Where JSON_CATEGORY_DATA_URL_STRING is my feed URL, which returns fine as:
[
{
"group":"For Sale",
"code":"SSSS"
},
{
"group":"For Sale",
"category":"Wanted",
"code":"SWNT"
}
]
I cannot seem to get a nice NSDictionary (or NSArray) out of the following code:
+ (NSDictionary *)downloadJSON
{
NSDictionary *json_string;
NSString *dataURL = [NSString stringWithFormat:#"%#", JSON_CATEGORY_DATA_URL_STRING];
NSLog(#"%#",dataURL);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:dataURL]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
json_string = [[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]autorelease];
NSDictionary *json_dict = (NSDictionary *)json_string;
NSLog(#"json_dict\n%#",json_dict);
NSLog(#"json_string\n%#",json_string);
return json_string;
}
I've read many posts on this, but am not getting it.
With IOS5 you can use NSJSONSerialization for serializing the JSON.
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
You can't just cast a string as a dictionary and expect it to parse the JSON. You must use a JSON parsing library to take that string and convert it into a dictionary.
I made a class that makes this task easier. It uses iOS 5's NSJSONSerialization. Clone it from github here.
You need to use JSON parser. here is the edited code
+ (NSDictionary *)downloadJSON
{
NSDictionary *json_string;
NSString *dataURL = [NSString stringWithFormat:#"%#", JSON_CATEGORY_DATA_URL_STRING];
NSLog(#"%#",dataURL);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:dataURL]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
json_string = [[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]autorelease];
//JSONValue is a function that will return the appropriate object like dictionary or array depending on your json string.
NSDictionary *json_dict = [json_string JSONValue];
NSLog(#"json_dict\n%#",json_dict);
NSLog(#"json_string\n%#",json_string);
return json_dict;
}
this should be the code to get the NSDictionary. but you json string is an array so instead use .
+ (NSArray *)downloadJSON
{
NSDictionary *json_string;
NSString *dataURL = [NSString stringWithFormat:#"%#", JSON_CATEGORY_DATA_URL_STRING];
NSLog(#"%#",dataURL);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:dataURL]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
json_string = [[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]autorelease];
NSArray *json_dict = [json_string JSONValue];
NSLog(#"json_dict\n%#",json_dict);
NSLog(#"json_string\n%#",json_string);
return json_dict;
}
Edit:
you need to use JSON.framework to call JSONValue method.
also you need to return json_dict instead of json_string as json_string is of NSString type and not NSDictionary or NSArray.
and dont autorelease it, as it is your class variable
create method to fetchjson data.Pass your url in urlwithstring.
-(void)fetchjsondata
{
NSString *login= [[NSString stringWithFormat:#"your url string"]stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"----%#", login);
NSURL *url = [NSURL URLWithString:[login stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//-- Get request and response though URL
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (data) {
dic_property= [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(#"%#", dic_property);
NSLog(#"counts=%d",[[dic_property objectForKey:#"Data"]count]);
}
else {
NSLog(#"network error, %#", [error localizedFailureReason]);
}
});
}];
}
call fetchjsonmethod in anywhere.
[NSThread detachNewThreadSelector:#selector(fetchdata) toTarget:self withObject:nil];