Can't parse JSON from data in connectionDidFinishLoading – Maybe it's too much data? - objective-c

I'm sending out a request to an external API and parsing a response with the SBJson parser. However, I suspect the response is so long, it is somehow getting jumbled.
In my mainviewcontroller.h file I set NSMutableData *receivedData; so that I can use it in the connection methods in the mainviewcontroller.m file.
However, after the connection finishes loading, I execute the following:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *dataString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSArray *allData = [dataString JSONValue];
}
However, I get a bunch of errors saying that the JSON is not properly formatted. So, when I look at the JSON, its very long – but here and there, there are problems... for example, the "updated_at" below.
{
"id": 7844333,
"position": 3,
"content": "Cell height is off by 5 pixels",
"created_at": "2012-06-04T20:31:30-05:00",
"updated_at": "2ator": {
"id": 98258,
"name": "Brian"
}
What I think happened above is that updated at has a value of "2012-06...etc" and the next key-value item would be creator : { id, name } but it somehow got jumbled into updated at.
Anyone having a similar problem? I don't think the problem is with the JSONValue because I nslog out the dataString before it gets parsed, and thats where I find the JSON errors.
What I mean by that is that NSString *dataString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; is just a long string, but has bad JSON in it because it is jumbled.

Are you using receivedData by more than one connection at once?
:)

I think your json is wrong. For checking that just put json file into the :
http://jsonlint.com/
If it is valid then:
Import the SBJSON framework classes into your project.And try the following code:
SBJSON *parser=[[SBJSON alloc]init];
NSDictionary * dictionary = [parser objectWithString:responseString];
this will give you data into dictionary then by using:
NSString *firstParseData=[dictionary objectForKey:#"your key"];
you can retrieve the data. Hope this will work in your case.

Related

Parse JSON String and array with NSJSONSerialization issue?

This is the code i have so far
// Parse data using NSJSONSerialization
NSError *error = nil;
NSArray *JsonArray = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error: &error];
if(!JsonArray)
{
NSLog(#"Error Parsing Data: %#", error);
}
else
{
for(NSDictionary *event in JsonArray)
{
if([[event description] isEqualToString:#"error"])
{
// Get error number? I am confused by this part
NSLog(#"Element: %#", [event objectForKey:#"error"]);
}
else
{
NSLog(#"Element: %#", [event description]);
}
}
}
this is the JSON Data that parses correctly:
[{data string}, {data strings}]
This only gives me the string "error" and not the int as well:
{"error":0}
I am echoing this data from a PHP script if that helps any. Am i just doing it wrong, or did i miss something?
Your problem is that when you receive an error, you get back an NSDictionary and not an NSArray. This should work:
if ([jsonObject isKindOfClass:[NSArray class]]) {
// no error: enumerate objects as you described above
} else if ([jsonObject isKindOfClass:[NSDictionary class]]) {
// error: obtain error code
NSNumber *errCode = jsonObject[#"error"];
} else {
// something bad's happening
}
Stylistic pieces of advice:
Don't call your object JsonArray, since it's not always an array. Call it jsonObject.
Don't start variable names with capital letters.
Would be great if you had posted the complete JSON document that you are trying to parse, because without that, there is absolutely no chance to figure out whether your code is anywhere near correct. The example [{data string}, {data strings}] that you gave is most definitely not a correct JSON document, so trying to parse it will return nil. {"error":0} is a dictionary with a single key "error" and a value 0. Having dictionaries with a single key is let's say unusual.
A JSON document contains either an array or object (using JSON terms) which will be turned either into an NSArray* or an NSDictionary*. You should know whether you expect an array or dictionary. If you expect an NSArray, check that [jsonObject isKindOfClass:[NSArray class]]. If you expect an NSDictionary, check that [jsonObject isKindOfClass:[NSDictionary class]]. If you don't do that then the wrong JSON document will either crash your app or produce total nonsense.
If you have an array then you will usually iterate through the elements of the array and handle each one in turn. If you have a dictionary you will usually look up keys that you know how to handle. What you are doing, iterating through an array of dictionaries, and checking for a dictionary with a key of "error", that's a very strange design of your JSON document.
And lookup what the "description" method does. "description" is what NSLog calls to find out what to print when it is asked to print an object. For an NSDictionary with a single key "error" and a value 0, it would return something like "error:0" which is of course not the same as "error".
NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error:&error];
NSLog(#"jsonDic: %#", [jsonDic objectForKey:#"string"]);

iOS and JSON nesting issue

I am working on an app for a game server company and part of the app requires the user to see a list of his or her game servers and whether or not they are online, offline, how many players on them, the server name, etc. This data is all found in a JSON file hosted on the web updated from a MySQL database.
Using the code below, I can't seem to get it working. Now, please understand it is one of my first times working with JSON and have no choice as this is what the client requested. I talked to my partner but he couldn't seem to debug the issue himself.
The error I get is something like:
No visible #interface for 'NSArray' declares the selector 'objectForKey:'
I've tried several versions of the code, all with no success.
It would be much appreciated if you could please help me debug this code and get it working along with the commented out section near the bottom updating the TableViewCells with the server name, players online, and status (0=offline, 1=online, 2=busy, 3=suspended, -1=unable to start).
Please note that the format of the JSON file must remain as is and is only possible to make very minor changes.
Thank you,
Michael S.
My header file:
http://pastebin.com/EkuwVSmY
My main file:
http://pastebin.com/09Ju0uDu
My JSON file:
{
"status": "OK",
"error": "",
"debug": "2 server(s)",
"result": {
"servers": [
{
"id": 1,
"title": "Test",
"players": 0,
"slots": 10,
"status": 3
},
{
"id": 2,
"title": "Creative Spawn",
"players": 0,
"slots": 5,
"status": -1
}
]
}
}
The block that gives me the error is:
NSArray *serverResults = [[news objectForKey:#"result"] objectForKey:#"servers"];
if ([[[serverResults objectAtIndex:indexPath.row] objectForKey:#"status"] isEqual:#"1"]) {
serverPlayers.text = #"10000000";
}
The error you get points out that you are working on an NSArray instead of a NSDictionary. There is no method objectForKey defined on an NSArray. So what you could do is to debug your Webservice response and check the Data types.
Regarding at your output from JSON it should be like that:
NSDictionary (Keys: status, error, debug, result, servers)
servers is an NSArray which has NSDictionaries as elements.
To get the title of a server:
NSDictionary *resultDict = [news objectForKey:#"result"];
NSArray *servers = [resultDict objectForKey:#"servers"];
NSDictionary *firstServer = [servers objectAtIndex:0]; // I fetch here the first server
NSString *titleOfFirstServer = [firstServer objectForKey:#"title"];
NSNumber *statusOfFirstServer = [NSNumber numberWithInt:[[firstServer objectForKey:#"status"] intValue];
To iterate over all servers you should do it like that:
NSDictionary *resultDict = [news objectForKey:#"result"];
NSArray *servers = [resultDict objectForKey:#"servers"];
for(NSDictionary *server in servers) {
NSString *title = [server objectForKey:#"title"];
NSNumber *status = [NSNumber numberWithInt:[[server objectForKey:#"status"] intValue];
NSLog(#"%#%d", title, [status intValue]);
}

Objective - C : How to read json? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to parse JSON in Objective-C?
I totally new with Objective c, I need to value of distance from google distance matrix json, but data that I can get from json just the first element,
so How to get value in distance this json?
{
"destination_addresses": [
"San Francisco, Californie, États-Unis",
"Victoria, BC, Canada"
],
"origin_addresses": [
"Vancouver, BC, Canada",
"Seattle, État de Washington, États-Unis"
],
"rows": [
{
"elements": [
{
"distance": {
"text": "1 732 km",
"value": 1732128
},
"duration": {
"text": "3 jours 23 heures",
"value": 340902
},
"status": "OK"
}
]
}
],
"status": "OK"
}
ok here is my sample code that just follow from JSON Tutorial :
I already use SBJSON , but I can only rows key, so how to get value in Distance key?
SBJSON *parser = [[SBJSON alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL
URLWithString:#"http://maps.googleapis.com/maps/api/distancematrix/json?origins=Vancouver+BC|Seattle&destinations=San+Francisco|Victoria+BC&mode=bicycling&language=fr-FR&sensor=false"]];
// Perform request and get JSON back as a NSData object
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSArray *statuses = [parser objectWithString:json_string error:nil];
for (NSDictionary *status in statuses)
{
NSLog(#"%# - %#", [status objectForKey:#"rows"]);
}
If you're targeting iOS 5 or OS X v10.7 then you can just use Apple's own NSJSONSerialization. You probably particularly want +JSONObjectWithData:options:error:. Using the built-in stuff is always preferable to third-party options because it eliminates the problem that the third party has no direct interest in fixing its bugs and no way to supply fixes for your application without your intervention.
If you want to support older devices then you probably want to import SBJSON — as as Rengers suggests — or any of the hundred others. For the reasons above I highly recommend you fall back on those only if NSJSONSerialization isn't available.
EDIT: it appears you already have SBJSON in place and are asking about how to traverse the results? From reading the JSON:
the root object will be a dictionary
within it, rows will be an array of dictionaries
each dictionary has an array named elements
each entry in that array is a dictionary
within that, distance is another dictionary
distance contains two strings, with keys "text" and "value"
So if you weren't to do any validation at all, to get to the distance dictionary you might do:
NSDictionary *result = [parser objectWithString:...];
NSLog(#"distance dictionary is: %#"
[[[[[
result objectForKey:#"rows"]
objectAtIndex:0]
objectForKey:#"elements"]
objectAtIndex:0]
objectForKey:#"distance"]
);
Based on your sample code, it looks like you may actually be getting an array of the sorts of dictionary posted rather than the dictionary directly. Obviously adapt if that's the case.
You need to use a JSON parser like SBJSON. This will create a NSDictionary/NSArray with all the values and keys. Then use the normal dictionary and array methods to access the data.

Have TouchJSON return mutable objects?

I am receiving some json from a web service. I parse this using the TouchJSON library.
I keep the data around for the user to change certain values and then I want to return it to the web service.
The JSON object I get contains NSDictionary Objects within the object, like this:
[
{
"id": null,
"created_at": 12332343,
"object": {
"name": "Some name",
"age" : 30
},
"scope": "it stuff",
"kind_id": 1,
"valid": true,
"refering_statement": {
"id": 95
},
"user": {
"id": 1
}
}
]
If I want to change values in this dictionary, I can't because the returned objects from TouchJSON are not mutable.
Is there a way to have have TouchJSON return mutable objects?
or is there a way to have Objective C make an NSDictionary and all its children mutable?
or do I have to just go through every NSDictionary in NSDictionary and copy all data into a mutable copy and then reinsert everything?
Hope someone can help me out on this one:) thanks in advance.
TouchJson has an option to return mutable object rather than normal. (I found it by looking at source code.) Default is to return its "copy", not "mutablecopy".
NSError *error = nil;
jsonString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF32BigEndianStringEncoding];
CJSONDeserializer *jsondeserializer = [CJSONDeserializer deserializer];
jsondeserializer.scanner.options = kJSONScannerOptions_MutableContainers;
NSMutableDictionary *jsonitems = [[NSMutableDictionary alloc] initWithDictionary:[jsondeserializer deserializeAsDictionary:jsonData error:&error]];
You can create a mutable dictionary from a dictionary like this.
(assuming your json parsed dictionary was named jsonDictionary)
NSMutableDictionary *userDictionary = [NSMutableDictionary dictionaryWithDictionary:jsonDictionary];
Hope that solves it for you.

parsing JSON using objective C?

I have spent 1 week studying objective C. Now I am quite confused at the dealing with data part.
My friend gave me a link
http://nrj.playsoft.fr/v3/getQuiz.php?udid=23423455&app=2
and ask me write a class to parse this JSON. I had no clue what parsing JSON means. but I have gone online and looked up. I could understand a basics of it and then I impletemented a punch of code to parse this JSON. Which is:
-
(void)parseURL
{
//create new SBJSON object
SBJSON *parser = [[SBJSON alloc] init];
NSError *error = nil;
//perform request from URL
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://nrj.playsoft.fr/v3/getQuiz.php?udid=23423455&app=2"]];
// Perform request and get JSON back as a NSData object
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
// Get JSON as a NSString from NSData response
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
// parse the JSON response into an object
NSDictionary *results = [parser objectWithString:json_string error:&error];
// array just for the "answer" results
NSArray *quizes = [results objectForKey:#"quiz"];
NSDictionary *firstQuiz = [quizes objectAtIndex:0];
// finally, the name key
NSString *extract = [firstQuiz objectForKey:#"extract"];
NSLog(#"this is: %#", [extract valueForKey:#"extract"]);
}
This is at the implementation file, but in the header file I could not declare any variables, it will print out some errors. I tried to run this, there is no errors, but I am not sure this code is correct or not. And my friend asked me to write a class into an existing project. I don't know what needs to be modified and what not. I am so blur right now. Could anyone pro in this give me a hand. ?
My sincere thanks.
Thanks for reply. I have downloading and added the JSON framework ealier too. I am just not sure where to begin and where to end, meaning the step I should do when I add JSON framework into it. I could understand the syntax but I am not sure about the steps I should do. I am a newbie in this.
If you support iOS 5.0+, you should use built-in NSJSONSerialization.
It is faster than TouchJSON.
You could just use TouchJSON: http://code.google.com/p/touchcode/wiki/TouchJSON
Or you could use this one: http://code.google.com/p/json-framework/
I'm sure there are others. I use TouchJSON... it's fast and has a good API.
I recommend working through Ray Wenderlich's MapKit tutorial, especially if you are a newbie. It covers several common iOS development issues, including parsing JSON data.
http://www.raywenderlich.com/2847/introduction-to-mapkit-on-ios-tutorial
"The Implementation" section is where his JSON feed is retrieved and then in "Plotting the Data" he uses the SBJson library to parse it.