3

I've read many Q/As on this problem but couldn't find an answer that fits my situation.

I retrieve a JSON response from a REST service I've created in PHP. This is my code:

NSURLResponse *response = nil;
NSError *theError1 = nil;
NSError *theError2 = nil;

NSURL *webServiceUrl = [NSURL URLWithString:@"http://..."];
NSURLRequest *request = [NSURLRequest requestWithURL:webServiceUrl cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
NSData *theData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&theError1];

NSString *dataString = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
NSLog(@"%@", dataString);

id json = [NSJSONSerialization JSONObjectWithData:theData options:NSJSONReadingAllowFragments | NSJSONReadingMutableContainers error:&theError2];
if (theError2 != nil)
    NSLog(@"%@", theError2);

When I invoke the REST call in the browser, I see the following response, which seems identical to what XCode logs:

{
  "Name": "REST Service",
  "Product": "REST Test",
  "Version": "1.0.0.0",
  "Copyright": "2013 Test Company"
}

When I execute above code, however, the following error is created and logged:

Error Domain=NSCocoaErrorDomain Code=3840 "The data couldn’t be read because it has been corrupted." (Invalid value around character 3.) UserInfo=0x100547430 {NSDebugDescription=Invalid value around character 3.}

What am I doing wrong?

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • Instead of logging `dataString`, log `theData` instead. It will show you a sequence of bytes, which should be more useful to debug your issue. Also, notice that you are not checking errors correctly: you need to test that the return value is `nil` before inspecting `theError2`. –  Sep 03 '13 at 07:51
  • I actually left that part out, because when debugging I saw that the adress of `json` was `0x0000...`. I'll analyze the byte sequence more detailed when I get home from work. – Thorsten Dittmar Sep 03 '13 at 08:06
  • 1
    Okay. I’ve just tested your JSON string and it works provided there are no funny characters in it, which `NSLog()` or `NSString` conversion might be hiding. –  Sep 03 '13 at 08:10
  • Hm. It works using the `NSJSONReadingAllowFragments | NSJSONReadingMutableContainers` flags? Then I assume I'm really having some strange characters in my response... – Thorsten Dittmar Sep 03 '13 at 08:17
  • I assembled a little tool in .NET on Windows that retrieves the JSON and it turns out that the data returned by the PHP scripts start with 4 times (!!) the BOM (239 187 191), one way would to solve my problem could now be to use `dataString` in the `NSJSONSerialized` instead of `theData`? – Thorsten Dittmar Sep 03 '13 at 08:48
  • There is no reason to change the data in any way to try to make it parse. If the server sends you invalid JSON, then it should be rejected, just as this parser does. Any fix needs to be made on the server. – gnasher729 Feb 20 '14 at 23:50

2 Answers2

1

OK, as always, checking the actual data instead of the string representation pays - thanks @Bavarious.

It turns out that the PHP script(s) in charge of creating the JSON were all "UTF8 with BOM", so PHP returned a BOM for every script involved.

Once I changed all the PHP files to "UTF8 without BOM" everything seems to be fine - need to test this on the MAC though.

Sorry to interrupt, keep up the good work.

(@Bavarious: If you'd like to write an answer, I'd be happy to upvote and accept it, as you pointed me to the solution).


Was able to parse the JSON now as expected. Making a mental note to always double-check the text file encoding.

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • Your answer is fine, and you should accept it. Note that BOM characters are invalid according to the JSON specification (RFC 4627) so, in theory, Web services should not include them. –  Sep 03 '13 at 09:11
  • I know. The thing is that PHP created these automatically based on the encoding of the PHP source file. – Thorsten Dittmar Sep 03 '13 at 09:13
  • I think NSJSONSerialization accepts _one_ BOM at the beginning of the text. But the next ones would be just the ordinary "zero width joiner" (can't remember exactly what it is called) which would be illegal in a JSON document. I think the lesson would be that if the parser says there's an "invalid value around character 3" then it's because there is an invalid value around character 3, whether you can see it or not. – gnasher729 Feb 20 '14 at 23:49
0
    NSURL *theURL = [NSURL URLWithString:@"http://yourdataurl"];

    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:theURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                       completionHandler:^(NSURLResponse *response,     NSData *data, NSError *connectionError) {
                               if (!connectionError) {
                                   NSError *error;

                                   NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

                                   NSData *theData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];

                                   NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:theData options:0 error:&error];


                                   if (!jsonResponse || error)
                                   {
                                       NSLog(@"Error");
                                   }
                                   else
                                   {
                                       // Everything is ok..
                                   }
                               }
                           }];
Jouhar
  • 288
  • 4
  • 9