views:

830

answers:

3

So I'm fetching a JSON string from a php script in my iPhone app using:

NSURL *baseURL = [NSURL URLWithString:@"test.php"];
NSError *encodeError = [[NSError alloc] init];
NSString *jsonString = [NSString stringWithContentsOfURL:baseURL encoding:NSUTF8StringEncoding error:&encodeError];
NSLog(@"Error: %@", [encodeError localizedDescription]);
NSLog(@"STRING: %@", jsonString);

The JSON string validates when I test the output. Now I'm having an encoding issue. When I fetch a single echo'd line such as:

{ "testKey":"é" }

(I'm aware I could\should be using NSUrlConnection for asynchronous fetching of data, but at this point in the app development, I don't really need it.)

The JSON parser works fine and I am able to create a valid JSON object. However, when I fetch my 2MB JSON string, I get presented with:

Error: Operation could not be completed. (Cocoa error 261.)

and a Null string. My PHP file is UTF8 itself and I am not using utf8_encode() because that seems to double encode the data since I'm already pulling the data as NSUTF8StringEncoding. Either way, in my single-echo test, it's the approach that allowed me to successfully log \ASDAS style UTF8 escapes when building the JSON object.

What could be causing the error in the case of the larger string?

Also, I'm not sure if it makes a difference, but I'm using the php function addslashes() on my parsed php data to account for quotes and such when building the JSON string.

A: 

For future reference, if you need to override the encoding, and you're working with streams without embedded NULs, something like this might be good (I've just written a rough sketch outline here, check this code is and does want you want before using it):

NSHTTPURLResponse* resp=nil;
NSData* jsonAsImmutableData = [NSURLConnection sendSynchronousRequest:
  [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://<whatever>"]]
  returningResponse:&resp error:NULL];

NSMutableData*modifiedData = [NSMutableData dataWithData:jsonAsImmutableData];

char extraNulls[7] =
  {0,0,0,0,0,0,0}; // is this defensive enough for your encoding?
[modifiedData appendBytes:extraNulls length:7];

NSString* jsonAsString = [NSString stringWithCString:[modifiedData bytes]
  encoding:<whatever your encoding is>];

But I expect your best course of action is to check that your server is both using and claiming to use UTF-8 encoding or some other Apple iPhone supported encoding.

EDIT

altered code comment.

martinr
I'm not quite sure I understand what you mean by defensive enough, or what this is accomplishing extra... could you elaborate a bit more please?
Mike A
oh if your encoding happens to be 32bit characters (ie eg UTF32) rather than 8bit characters, to get an end 32bits all null (to terminate the C string, required by the stringWithCString: API call) can only be ensured by adding 7 NUL characters (4 needed if perfectly aligned; an extra 3 needed in worst case of misalignment). If its just ASCII 8bit encoding, 1 NUL byte will suffice, but I stuck extra in because I don't know what encoding you're using.
martinr
I of course mean US ASCII with high bit clear (so 7bit codes in an eight bit byte). And I'm not sure how many Null bytes are required really, even for UTF32. I'm handing checking such over to you, to check for your (particular) encoding. Thankyou..!
martinr
This didn't seem to change anything, unfortunately.
Mike A
Well once you have the character bytes in memory, you can *manually* with some C bit-twiddling code in a loop to check the encoding rules to see what is broken with the encoding. My experience with the iPhone API and character encoding is that even though one might assume it is supposed to helpfully replace any bad encoding with the replacement character, it does break and return no helpful output if you give it even slightly broken encoding, and I had to write my own encoding pre-validators and pre-fixers in some cases.
martinr
EDIT: "have seen it break" instead of "does break".
martinr
A: 

Turns out the answer to all your problems is just keeping things simple. Instead of trying to build the JSON string myself and worrying about printing all the proper syntax and formatting, I finally decided to use the json_encode() PHP function.

By default, it seems the json_encode() function applies the utf8_encode() wrapper, so be weary of double encoding your data.

I ignored using the default json_encode() because I thought I couldn't natively handle dictionaries in PHP. Turns out you can.

Example of how to declare a dictionary in PHP:

$dictionary = array("key1"=>$var1, "key2"=>array());

json_encode($dictionary) would then build :

{"key1":"<var1>", "key2":[]}
Mike A
+1  A: 

Don't know if this is your problem, but I just had a similar thing (stringWithContentsOfFile, no JSON), and the problem was that the file had CRLF (windows) line-endings and Western-whatever-it's-called encoding. I used SubEthaEdit to convert to LF and UTF-8, and everything works fine.

Olie