How to return a raw string of text/json from aspnet mvc and parse it with the iPhone JSON library - objective-c

I have a web service that returns a raw string of JSON data (sample below)
{'d':{'success':true,'msg':null,'data':[{'productId':'4b7fcb0f818e4a4abaf5b3c78654b631','id':4283}]}}
... And when I attempt to parse this JSON using the popular JSON library for iPhone development I notice a problem in the following method
- (id)objectWithString:(NSString *)repr {
[self clearErrorTrace];
if (!repr) {
[self addErrorWithCode:EINPUT description:#"Input was 'nil'"];
return nil;
}
depth = 0;
c = [repr UTF8String];
id o;
if (![self scanValue:&o]) {
return nil;
}
// more code ...
}
When I hit the last if statement shown I show c to be a valid char (showing the json values inside as expected) but I notice that after o is defined I return nil inside that last if causing this error from the library
#import "NSString+SBJSON.h"
#import "SBJsonParser.h"
#implementation NSString (NSString_SBJSON)
- (id)JSONValue
{
SBJsonParser *jsonParser = [SBJsonParser new];
id repr = [jsonParser objectWithString:self];
if (!repr)
NSLog(#"-JSONValue failed. Error trace is: %#", [jsonParser errorTrace]);
[jsonParser release];
return repr;
}
#end
Any idea what might be wrong with taking a string of what looks like json and trying to parse it like the below?
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *json = [responseString JSONValue]; //here it all fails
}
Just as a note- this works without error when I query a valid json web service from a large company. My app is aspnet mvc and the return line is simply
return this.Content(jsonStringValue, "text/json");

You should use the built in JSON result object from your action method instead of the Content() method:
return Json(data);
It will automatically serialize your object to be valid JSON.

It would be useful to provide actual error messages/traces.
At any rate, your JSON string is not valid: a string in JSON is quoted within double quotes, not single quotes. The following is valid JSON:
{"d":{"success":true,"msg":null,"data":[{"productId":"4b7fcb0f818e4a4abaf5b3c78654b631","id":4283}]}}
Edit: note that your JSON (top level) data represents an object, not an array. Hence
NSArray *json = [responseString JSONValue];
should be replaced with
NSDictionary *json = [responseString JSONValue];
because SBJSON represents JSON objects as dictionaries.

Related

RestKit: mapping BOOL and integer values

I'm evaluating RestKit to use in my project. I've created a simple app that loads some JSON and maps it into Objective-C objects. I'm having a problem correctly mapping a JSON object that has numeric and logical fields. E.g.
{
"integerValue":"5",
"booleanValue":"YES",
}
I want these to map to the following properties in my data object:
#property int integerValue;
#property BOOL booleanValue;
It didn't work out of the box, so I've created a value transformer for that:
[_activityMapping setValueTransformer:[RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class inputValueClass, __unsafe_unretained Class outputValueClass) {
if([inputValueClass isSubclassOfClass:[NSString class]] && [outputValueClass isSubclassOfClass:[NSNumber class]]) {
return YES;
}
else {
return NO;
}
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputClass, NSError *__autoreleasing *error) {
if([[inputValue class] isSubclassOfClass:[NSString class]] && [outputClass isSubclassOfClass:[NSNumber class]]) {
NSString *inputString = (NSString *)inputValue;
if([inputString isEqualToString:#"YES"] || [inputString isEqualToString:#"NO"]) {
*outputValue = [NSNumber numberWithBool:[inputString boolValue]];
}
else {
*outputValue = [NSNumber numberWithInt:[inputString intValue]];
}
}
else {
*outputValue = [inputValue copy];
}
return YES;
}]];
This code works, but looks ugly. Note how I have to check the input value to see if it's a boolean or an integer. Any suggestions on an elegant solution to this problem?
Please note that I'm using RestKit. I do know about NSJSONSerialization and know how to parse JSON in code. If you suggest a non-RestKit solution, please explain why do you not recommend using RestKit.
The issue is not occurring at the RestKit level but at the JSON level itself.
According to the JSON spec Boolean values should be represented with true/false not YES/NO. If you update your JSON to be semantically correct then RestKit should do the right thing.
Ok. So according to my understanding of your answer, your main problem lies in mapping the data in the JSON object to their very own designated variables.
So, I'd recommend using the conventional NSJSONSerialization approach.
So, first up. You need to store your JSON object in an NSData object. Now, you're most likely downloading the data from a simple URL. So, this is what you'd do :
//This part is just to download the data. If you're using another method - that's fine. Just make sure that the download is in NSData format
NSURL *url = [[NSURL alloc] initWithString : #"YOUR_URL_HERE"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL : url];
NSData *jsonData = [NSURLConnection sendSynchronousRequest:request
returningResponse:nil
error:nil];
Now, you need to map those to the NSDictionary... Here's how :
//This is the actual NSJSONSerialization part.
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableLeaves
error:nil];
Now, just map the values to your designated properties.
_integerValue = (int)[jsonDict objectForKey:#"integerValue"];
_booleanValue = (BOOL)[jsonDict objectForKey:#"booleanValue"];

Issue with parsing JSON data

I am trying to convert a string into a json object and am unsure why this is not working. When I nslog the output I am told that urldata is not valid for json serialization however when looking at the string it looks to me like valid json. I have also tried encoding it to an utf8 however it still won't serialize. Am I missing something here? - Note unnecessary code omitted from post.
Get request
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
NSDictionary *tempDict = [NSDictionary alloc];
Parsing
if ([NSJSONSerialization isValidJSONObject:urlData] ) {
NSLog(#"is valid");
tempDict = [NSJSONSerialization JSONObjectWithData:urlData kniloptions error:&error];
}
NSLog(#"is not valid");
Definition:
isValidJSONObject:
Returns a Boolean value that indicates whether a given object can be converted to JSON data.
As you are already mentioning in your question, isValidJSONObject
returns a Boolean value that indicates whether a given object can be
converted to JSON data
In your case, you don't want to create JSON data, but instead create a dictionary out of JSON data. :
tempDict = [NSJSONSerialization JSONObjectWithData:urlData
options:NSJSONReadingMutableContainers
error:&error];
if (!tempDict) {
NSLog(#"Error parsing JSON: %#", error);
}

Parsing JSON value in objective c error?

I use ASIHTTPRequest to get a token, and I receive this string:
{Token:[{"new":"jkajshdkjashdjhasjkdhjkhd+sd==sfbjhdskfbks+sdjfbs=="}]}
I used JSON Framework from here: http://stig.github.com/json-framework.
This is the code after I get the string:
- (void)requestFinished:(ASIHTTPRequest *)req
{
NSString *responseString = [req responseString];
NSLog(#"Response: %#", [req responseString]);
// Parse the response
SBJsonParser *jsParser = [SBJsonParser new];
id content = [responseString JSONValue];
if(!content){
// Parsing error
NSLog(#"%#", jsParser.errorTrace);
}
}
NSLog(#"ID: %#", content);
I receive this error:
-JSONValue failed. Error trace is: (
"Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \"Object key string expected\" UserInfo=0x5a418a0 {NSLocalizedDescription=Object key string expected}"
)
My guess is that, the JSON Framework that I am using cant understand the response string format:
{Token:[{"new":"jkajshdkjashdjhasjkdhjkhd+sd==sfbjhdskfbks+sdjfbs=="}]}
Is there any other way to parse the value?
Happy programming,
Johnie
It was looking for a start and end " surrounding Token.
{"Token":[{"new":"jkajshdkjashdjhasjkdhjkhd+sd==sfbjhdskfbks+sdjfbs=="}]}
There's a bug in this code:
SBJsonParser *jsParser = [SBJsonParser new];
id content = [responseString JSONValue];
if(!content){
// Parsing error
NSLog(#"%#", jsParser.errorTrace);
}
You instantiate a parser, jsParser, but don't use it to parse the string. (The JSONValue method instantiates a new parser internally.) Then you read the error from the parser that was not used to parse the string.

Trouble getting results from json message in objective c using JSON.Framework

I am attempting to read a json message using the json.framework. The message is a nested collection of meeting details. My desire is to iterate thought all of the meetings and create local meeting objects with the details read from the message. I see to get list of the 15 meetings that are in the json results but can't get the individual values from the results.
Here is my sample code. I am using a file for the json message so that I don't have to involve the server in this test. The json message can be downloaded here.
-(void)TestParse:(NSString *)response
{
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"conference_calls" ofType:#"json"];
NSString *fileContent =[[NSString alloc]initWithContentsOfFile:filePath];
parser = [SBJsonParser new];
NSArray *results = [parser objectWithString:fileContent];
NSLog(#"Number of itmems in the results: --> %i", [results count]);
for(NSDictionary *conf in results){
//Load local objects with the values of the Conf info.
NSLog(#"This the description %# ",[c valueForKey:"phone_number"]);
NSLog(#"Number of Items in Dic: %i",[conf count]);
NSLog(#"File contents: %#",[conf description]);
}
The structure of your json is an array of dictionaries. But each dictionary has just a single key called "conference_call", with the value for that key being another dictionary with all the details of that call.
So something like this should work:
for (NSDictionary* call in results) {
// get the actual data for this call
NSDictionary *callDetails = [call objectForKey:#"conference_call"];
NSLog (#"Location is %#", [callDetails objectForKey:#"location"]);
}
Hope that helps.

How to parse nested JSON objects with JSON framework and Objective-C/iPhone/Xcode?

I'm writing an iPhone native app using the JSON framework.
My app is accessing web services using JSON. The JSON data we send has nested objects, below is an example of the data served up:
{
"model": {
"JSONRESPONSE": {
"authenticationFlag": true,
"sessionId": "3C4AA754D77BFBE33E0D66EBE306B8CA",
"statusMessage": "Successful Login.",
"locId": 1,
"userName": "Joe Schmoe"
}
}
}
I'm having problem parsing using the objectForKey and valueForKey NSDictionary methods. I keep getting invalidArgumentException runtime errors.
For instance, I want to query the response data for the "authenticationFlag" element.
Thanks,
Mike
Seattle
It is hard to tell without some more details (e.g. the JSON parsing code that you are using), but two things strike me as possible:
you are not querying with a full path. In the case above, you'd need to first get the enclosing model, the json response, and only then ask the json response dictionary for the authenticationFlag value:
[[[jsonDict objectForKey:#"model"]
objectForKey:#"JSONRESPONSE"] objectForKey:#"authenticationFlag"]
perhaps you're using c-strings ("") rather than NSStrings (#"") as keys (although this would likely crash nastily or just not compile). The key should be something than can be cast to id.
While possible, both are probably false, so please include more detail.
The following is taken directly from Dan Grigsby's Tutorial at - http://mobileorchard.com/tutorial-json-over-http-on-the-iphone/ - Please attribute, stealing is bad karma.
Fetching JSON Over HTTP
We’ll use Cocoa’s NSURLConnection to issue an HTTP request and retrieve the JSON data.
Cocoa provides both synchronous and asynchronous options for making HTTP requests. Synchronous requests run from the application’s main runloop cause the app to halt while it waits for a response. Asynchronous requests use callbacks to avoid blocking and are straightforward to use. We’ll use asynchronous requests.
First thing we need to do is update our view controller’s interface to include an NSMutableData to hold the response data. We declare this in the interface (and not inside a method) because the response comes back serially in pieces that we stitch together rather than in a complete unit.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
IBOutlet UILabel *label;
NSMutableData *responseData;
}
To keep things simple, we’ll kick off the HTTP request from viewDidLoad.
Replace the contents of :
#import "JSON/JSON.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"XYZ.json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (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];
}
- (void)dealloc {
[super dealloc];
}
#end
This mostly boilerplate code initializes the responseData variable to be ready to hold the data and kicks off the connection in viewDidload; it gathers the pieces as they come in in didReceiveData; and the empty connectionDidFinishLoading stands ready to do something with the results.
Using The JSON Data
Next, we’ll flesh out the connectionDidFinishLoading method to make use of the JSON data retrieved in the last step.
Update the connectionDidFinishLoading method :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *luckyNumbers = [responseString JSONValue];
NSMutableString *text = [NSMutableString stringWithString:#"Lucky numbers:\n"];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#\n", [luckyNumbers objectAtIndex:i]];
label.text = text;
}
It creates an NSArray. The parser is very flexible and returns objects — including nested objects — that appropriately match JSON datatypes to Objective-C datatypes.
Better Error Handling
Thus far, we’ve been using the the convenient, high-level extensions to NSString method of parsing JSON. We’ve done so with good reason: it’s handy to simple send the JSONValue message to a string to accessed to the parsed JSON values.
Unfortunately, using this method makes helpful error handling difficult. If the JSON parser fails for any reason it simply returns a nil value. However, if you watch your console log when this happens, you’ll see messages describing precisely what caused the parser to fail.
It’d be nice to be able to pass those error details along to the user. To do so, we’ll switch to the second, object-oriented method, that the JSON SDK supports.
Update the connectionDidFinishLoading method in :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSArray *luckyNumbers = [json objectWithString:responseString error:&error];
[responseString release];
if (luckyNumbers == nil)
label.text = [NSString stringWithFormat:#"JSON parsing failed: %#", [error localizedDescription]];
else {
NSMutableString *text = [NSMutableString stringWithString:#"Lucky numbers:\n"];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#\n", [viewcontroller objectAtIndex:i]];
label.text = text;
}
}
Using this method gives us a pointer to the error object of the underlying JSON parser that we can use for more useful error handling.
Conclusion :
The JSON SDK and Cocoa's built-in support for HTTP make adding JSON web services to iPhone apps straightforward.
NSString* aStr;
aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSDictionary *dictionary = [aStr JSONValue];
NSArray *keys = [dictionary allKeys];
// values in foreach loop
for (NSString *key in keys) {
NSArray *items = (NSArray *) [dictionary objectForKey:key];
for (id *item in items) {
NSString* aStrs= item;
NSLog(#" test %#", aStrs);
NSDictionary *dict = aStrs;
NSArray *k = [dict allKeys];
for (id *it in k) {
NSLog(#"the child item: %#", [NSString stringWithFormat:#"Child Item -> %# value %#", (NSDictionary *) it,[dict objectForKey:it]]);
}
Now, Objective c has introduced in build class for JSON Parsing.
NSError *myError = nil;
NSDictionary *resultDictionary = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html
So utilize this class and make your application error free...:)