iOS 5 JSON Parsing Results in Cocoa Error 3840 - ios

I'm having a hard time parsing the below JSON string on iOS 5.
{"States": [{"Name": "Arizona","Cities": [{"Name": "Phoenix"}]},{"Name": "California","Cities": [{"Name": "Orange County"},{"Name": "Riverside"},{"Name": "San Diego"},{"Name": "San Francisco"}]},{"Name": "Nevada","Cities": [{"Name": "Las Vegas"}]}]}
And here's my code:
- (void) parseJson {
NSError *jsonError = nil;
NSData *jsonData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Locations-JSON" ofType:#"rtf"]];
if (jsonData) {
NSDictionary *jsonObjects = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&jsonError];
if (jsonError) {
NSLog(#"JSON Error: %#", [jsonError localizedDescription]);
return;
}
NSLog(#"%#", jsonObjects);
}
}
I keep getting this error:
JSON Error: The operation couldn’t be completed. (Cocoa error 3840.)
I'd appreciate some help on this because I clearly and incapable of fixing this.

One thing that strikes me as incorrect is this:
[[NSBundle mainBundle] pathForResource:#"Locations-JSON" ofType:#"rtf"]
Your data is an RTF file?? It should be a txt file (or any other sort of plain text file). RTF files usually contain text formatting data, like this:
{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
\f0\fs24 \cf0 \{"States": [\{"Name": "Arizona","Cities": [\{"Name": "Phoenix"\}]\},\{"Name": "California","Cities": [\{"Name": "Orange County"\},\{"Name": "Riverside"\},\{"Name": "San Diego"\},\{"Name": "San Francisco"\}]\},\{"Name": "Nevada","Cities": [\{"Name": "Las Vegas"\}]\}]\}}
When I read that in as a data and try to parse it as JSON, I get the 3840 error you're seeing. That error's description says:
The data couldn’t be read because it has been corrupted. (No string key for value in object around character 2.)
So what it looks like to me is that you don't actually have JSON. You have RTF data.

I had hit a similar problem. My JSON parser works intermittently when I download the JSON data from a server. Did you get your JSON data from this function?
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
The NSData returned from this function could be partial data. You need to appendData to an instance variable with type: NSMutableData. Then you process your JSON in another function as follows:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
Reading this article for the details. It works for me
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html

I was able to troubleshoot my JSON 3840 error by converting the NSData object to an NSString:
NSError *error;
NSObject *object = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
if (object == nil) {
NSString *serverResponse = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
NSLog(#"\n\nError:\n%#\n\nServer Response:\n%#\n\nCrash:", error.description, serverResponse);
[NSException raise:#"Invalid Data" format:#"Unable to process web server response."];
}

If you arrived here because of the JSON and not because of the RTF , please check out this answer :
IOS JSON Deserialization failure - STIG/NSJSONSerializer

Related

WatchConnectivity attempting to send data to Watch app

I'm trying to send data from my iOS app to the Watch app. I'm using the updateApplicationContext for this.
I have a json file that I converted in NSDictionary and tried to send it. But there is an error, here it is:
[WCSession updateApplicationContext:error:]_block_invoke failed due to WCErrorCodePayloadUnsupportedTypes
The file is correctly read.
Now here is the code that tries to send the data.
NSString *fileName = [[NSBundle mainBundle] pathForResource:#"data"
ofType:#"json"];
NSLog(fileName);
if (fileName) {
NSData *jsonData = [[NSData alloc] initWithContentsOfFile:fileName];
NSDictionary *data = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:&error];
if (error) {
NSLog(#"Something went wrong! %#", error.localizedDescription);
}
else {
NSLog(#"Rsens info: %#", data);
[WatchSessionManager.sharedManager updateApplicationContextWithApplicationContext:#{#"json": data} error:&error];
}
}
else {
NSLog(#"Couldn't find file!");
}
I read somewhere that the types that we could send were limited, but the dictionary was allowed. I'm sending a dictionary though.
Can you find what is the error?
[SOLUTION]
I found out that there were values of type long in my dictionary. In my JSON I had some properties that were transtyped in type long. Here is one of the properties before:
"state": 0
I just put my numbers in string quotes.
"state":"0"
Check the dictionary's content.
WatchConnectivity dictionaries can only contain property list types.

How to parse JSON data from textual file in Objective-C

I know, JSON parsing questions are asked over and over again, but still I can't find any answer to this one.
I've been trying to read and parse a textual JSON file using NSJSONSerialization to no avail.
I've tried using the same JSON data from a NSString and it did work.
Here's the code:
NSError *error;
NSString *jsonString1 = [NSString stringWithContentsOfFile:jsonFilePath
encoding:NSUTF8StringEncoding
error:&error];
NSData *jsonData1 = [jsonString1 dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonObject1 = [NSJSONSerialization JSONObjectWithData:jsonData1
options:0
error:&error];
NSString *jsonString2 = #"{\"key\":\"value\"}";
NSData *jsonData2 = [jsonString2 dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonObject2 = [NSJSONSerialization JSONObjectWithData:jsonData2
options:0
error:&error];
- The text file contains one line: {"key":"value"}
- jsonString1 = #"{"key":"value"}"
- jsonString2 = #"{\"key\":\"value\"}"
- jsonData1 is 23 bytes in size
- jsonData2 is 15 bytes in size
- jsonObject1 is nil and I get error code 3840
- jsonObject2 is a valid dictionary
Seems like the problem is with reading the file, since the NSStrings and NSDatas differ, but what am I doing wrong here and how can I fix it?
Most likely you file contains some unprintable characters (e.g. \0) that trigger the failure. Printing the error message will tell you at what position the first invalid characters occurs.
For example, try printing "{\"key\":\u{0000}\"value\"}" and you'll seem to get a valid JSON, however decoding it fails.
I always do a check on the return value when doing anything with NSUTF8StringEncoding and if nil, then try NSASCIIStringEncoding:
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
if (jsonString == nil) {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSASCIIStringEncoding];
}
return jsonString;

How to convert escaped JSON-string to NSString?

I am working with a http-server, which return JSON-string in response body:
"Some text.\nSome text.\n\t\"Some text in quotes.\""
I need remove quotes in the start and end of string, and I need unescape special symbols. I make category for NSString, but I think that it is wrong implementation: https://gist.github.com/virasio/59907e087f859e6c1723
I have other idea. I can use NSJSONSerialization:
NSString *sourceString = #"\"Some text.\\nSome text.\\n\\t\\\"Some text in quotes.\\\"\"";
NSString *jsonObject = [NSString stringWithFormat:#"{ \"value\" : %# }", sourceString];
NSDictionary *object = [NSJSONSerialization JSONObjectWithData:[jsonObject dataUsingEncoding:NSUTF8StringEncoding options:0 error:NULL]];
NSString *result = [object objectForKey:#"value"];
But... It is not good too.
By default, Foundation will only parse JSON objects or arrays, but it can parse strings, numbers and booleans if you tell it to accept JSON fragments:
NSData *data = [#"\"Some text.\\nSome text.\\n\\t\\\"Some text in quotes.\\\"\""
dataUsingEncoding:NSUTF8StringEncoding];
id result = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:NULL];
NSLog(#"[result class] = %#", [result class]);
NSLog(#"result = %#", result);
Yields:
[result class] = __NSCFString
result = Some text.
Some text.
"Some text in quotes."
This is actually one of the cases where passing error really helps. I had tried without passing NSJSONReadingAllowFragments and got a very clear error message:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.)

NSData to NSString with JSON response

NSData* jsonData is the http response contains JSON data.
NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"jsonString: %#", jsonString);
I got the result:
{ "result": "\u8aaa" }
What is the proper way to encoding the data to the correct string, not unicode string like "\uxxxx"?
If you convert the JSON data
{ "result" : "\u8aaa" }
to a NSDictionary (e.g. using NSJSONSerialization) and print the dictionary
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSLog(#"%#", jsonDict);
then you will get the output
{
result = "\U8aaa";
}
The reason is that the description method of NSDictionary uses "\Unnnn" escape sequences
for all non-ASCII characters. But that is only for display in the console, the dictionary is correct!
If you print the value of the key
NSLog(#"%#", [jsonDict objectForKey:#"result"]);
then you will get the expected output
說
I don't quite understand what the problem is. AFNetworking has given you a valid JSON packet. If you want the above code to output the character instead of the \u… escape sequence, you should coax the server feeding you the result to change its output. But this shouldn't be necessary. What you most likely want to do next is run it through a JSON deserializer…
NSDictionary * data = [NSJSONSerialization JSONObjectWithData:jsonData …];
…and you should get the following dictionary back: #{#"result":#"說"}. Note that the result key holds a string with a single character, which I'm guessing is what you want.
BTW: In future, I suggest you copy-paste output into your question rather than transcribing it by hand. It'll avoid several needless rounds of corrections and confusion.

NSJSONSerialization sometimes returns null in async request

I've been developing app, that makes asynchronous requests for JSON type of data. Lately, I found strange bug in my code and I can't tell why it is happening.
Ok to the code!
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSError *error=nil;
result = [NSJSONSerialization JSONObjectWithData:retrievedData options:kNilOptions error:&error];
NSLog(#"Result %#",result);
NSLog(#"Retrieved data %#",retrievedData);
}
Result is NSDictionary, retrievedData is NSMutableData.
99% of the time, it works fine, connectionDidFinishLoading gets called, and my result is populated. However, in that 1% of the time retrieved data is filled with data, but my result is null.(as you can see on the picture. Could anyone help me please?
Edit: I get following error
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (Garbage at end.) UserInfo=0x753e5c0 {NSDebugDescription=Garbage at end.}
It would help if you take a look at the error first, to see if that gives any indication of what is wrong.
[edit]
Your error mentions the reason: Garbage at end..
The response from the webserver is not valid JSON, it contains invalid characters at the end of the output.
I had the same issue. First, look what is a data that wasn't parsed correctly - in my case I did
NSString *str = [[NSString alloc] initWithData:retrievedData encoding:NSUTF8StringEncoding];
In my case the reason was - if server sent a few socket.write()'s in a row - all data was received in one single chunk, like
{first:json}{second:json}..
of course this cannot be parsed as one single json, so I have to introduce delimiter, and split the received buffer into correct chunks.
This is a little late but nothing online worked for me until I did this:
NSString * dataInString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSRange range = [dataInString rangeOfString:#"}" options:NSBackwardsSearch];
if(range.location != NSNotFound)
dataInString = [dataInString substringWithRange:NSMakeRange(0,range.location+1)];
Always worked since then.
I had the same error, the problem was my server was attaching some extra lines to my json response, that would not appear when i will get the response in my browser. Using curl from terminal you can see the actual output.
The hack was to truncate the extra characters. with json either you have an array or dictionary. Depending on your json structure, you can use the code (as above answer) but look for the comment in line 2
NSString * str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSRange range = [str rangeOfString:#"}" options:NSBackwardsSearch]; // } for dictionary, ] for array
if(range.location != NSNotFound)
str = [str substringWithRange:NSMakeRange(0,range.location+1)];
once you get your string clean from garbage data, you can convert it to data again.
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
and now from this data you can get your array or dictionary.
id jsonObject = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions error:&error]; // i used id to be general, you can use array or dictionary structure or later cast this object to as per json

Resources