How to parse JSONP in Objective-C?

2019-04-02 12:26发布

I am retrieving JSON information for an API and it says on the API that it is in JSON but I noticed it is in JSONP or "json with padding" as some call it. I tired to look everywhere to find how to parse this but no luck. The information I am trying to receive is this:

 ({"book":[{"book_name":"James","book_nr":"59","chapter_nr":"3","chapter":
 {"16":{"verse_nr":"16","verse":"For where envying and strife is, there is confusion and 
 every evil work."}}}],"direction":"LTR","type":"verse"});

The link to the data is https://getbible.net/json?p=James3:16, so you can look at it directly.

This is the code I am using to try to retrieve the JSON Data and parse it into a NSMutableDictionary.

-(void)fetchJson {
    NSString *currentURL = [NSString stringWithFormat:@"https://getbible.net/json?p=James"];
    NSURL *url = [NSURL URLWithString:currentURL];
    NSData *data = [[NSData alloc]initWithContentsOfURL:url];
    NSURLRequest *theRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
    NSMutableData *receivedData = [[NSMutableData alloc] initWithLength:0];
    NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];

    [receivedData setLength:0];
    NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url MIMEType:@".json" expectedContentLength:-1 textEncodingName:nil];
    expectedTotalSize = [response expectedContentLength];


    if ([data length] !=0) {
        NSLog(@"appendingData");
        [receivedData appendData:data];

        if(connection){
            NSLog(@"Succeeded! Received %lu bytes of data",(unsigned long)[receivedData length]);
        }

        NSError *error;
        NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];

        if(jsonResponse){
            NSArray *responseArr = [jsonResponse mutableCopy];
            NSLog(@"%lu",(unsigned long)[responseArr count]);
        }else if (!jsonResponse){
            //do internet connection error response
        }
    }
 }

The results I am getting back from putting a breakpoint in the code is:

  • jsonResponse returns NULL
  • NSError NSCocoaErrorDomain code - 3840
  • but my NSData *data is returning 15640 bytes.

My console is displaying this from the NSLogs I used for debugging:

   2014-04-20 01:27:31.877 appendingData
   2014-04-20 01:27:31.879 Succeeded! Received 15640 bytes of data

I am receiving the data correctly but I am not parsing it correctly I know the error is because the JSON is in JSONP format. If anyone could please help with this I would appreciate it so much. I have tired to give as much detail on this question as I can but if you need more information just let me know so I can add it and make this as clear as possible.

3条回答
仙女界的扛把子
2楼-- · 2019-04-02 12:52

One problem is that the data you're downloading has extraneous information at the beginning and end. The JSON being delivered by your URL is:

({"book":[{"book_name":"James","book_nr":"59","chapter_nr":"3","chapter":{"16":{"verse_nr":"16","verse":"For where envying and strife is, there is confusion and every evil work."}}}],"direction":"LTR","type":"verse"});

As the error message you're seeing indicates: you need to remove the initial ( from the beginning of the string and the ); from the end so that your JSON will start with the dictionary that your code expects. You can do this by calling subdataWithRange: on your NSData object:

NSData* jsonData = [data subdataWithRange:NSMakeRange(1, data.length-3)];
NSDictionary* jsonResponse = [NSJSONSerialization JSONObjectWithData:jsonData
                                                             options:0
                                                               error:&error];
查看更多
孤傲高冷的网名
3楼-- · 2019-04-02 13:02

Your code has at least two separate attempts to download the data. Neither is really correct. The code also only works with JSON, not JSONP.

Try this:

NSURL *url = [NSURL URLWithString:@"https://getbible.net/json?p=James"];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if (data) {
        NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSRange range = [jsonString rangeOfString:@"("];
        range.location++;
        range.length = [jsonString length] - range.location - 2; // removes parens and trailing semicolon
        jsonString = [jsonString substringWithRange:range];
        NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];

        NSError *jsonError = nil;
        NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&jsonError];
        if (jsonResponse) {
            // process jsonResponse as needed
        } else {
            NSLog(@"Unable to parse JSON data: %@", jsonError);
        }
    } else {
        NSLog(@"Error loading data: %@", error);
    }
}];
查看更多
一夜七次
4楼-- · 2019-04-02 13:05

Just to update everyone, the NSURLRequest has been deprecated in iOS9. I tried the answer by @rmaddy, and I didn't receive anything either (just like what @lostAtSeaJoshua was encountering I guess). I have updated rmaddy's answer to reflect the NSURLSession implementation that has (I think) replaced NSURLRequest:

    NSURLSession *session = [NSURLSession sharedSession];
    NSURL *url = [NSURL URLWithString:@"http://somerandomwebsite.com/get.php?anotherRandomParameter=5"];
    [[session dataTaskWithURL:url
            completionHandler:^(NSData *data,
                                NSURLResponse *response,
                                NSError *error) {
                // handle response
                if (data) {
                    NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                    NSLog(@"stringJSONed: %@",jsonString);

//Do something with the received jsonString, just like in @ rmaddy's reply
                } else {
                    NSLog(@"Error loading data: %@", error);
                }
            }] resume];

Just a heads up notice, when I first ran it, it gave me the security error. What you need to do (if you are using http) is to add this to your plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

I have to mention that after the NSAllowArbitraryLoads key, there are most probably other keys and values, such as NSExceptionDomain. But they're not really relevant to this answer I think. If you need to look further, let me know and I will dig deeper :)

查看更多
登录 后发表回答