Сбой NSJSONSerialization при извлечении данных с определенного URL-адреса

Я пытаюсь получить данные с этого URL-адреса, но у меня возникают проблемы. Почему это вылетает на строке NSJSONSerialization? Есть ли лучший способ загрузить информацию с этого сайта?

РЕДАКТИРОВАТЬ: я изменил jsonArray с NSArray на NSDictionary, но он все еще падает в том же месте. Есть ли другой способ загрузить эти данные?

NSString *url=@"https://api.p04.simcity.com/simcity/rest/users/search/J3d1.json";

NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];

NSURLResponse *resp = nil;
NSError *err = nil;

NSData *response = [NSURLConnection sendSynchronousRequest: theRequest returningResponse: &resp error: &err];

NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData: response options: NSJSONReadingMutableContainers error: &err];

NSLog(@"%@",jsonArray);

Для справки, JSON:

{
    "users": [
        {
            "uri": "/rest/user/20624",
            "tutorialState": 0,
            "nucleusId": 20624,
            "id": 20624,
            "screenName": "R3DEYEJ3D1",
            "lastLogin": 1362666027000,
            "isOnline": "true",
            "avatarImage": "https://api.p04.simcity.com/simcity/rest/user/20624/avatar",
            "cities_count": 0,
            "canChat": "true"
        },
        {
            "uri": "/rest/user/46326",
            "tutorialState": 0,
            "nucleusId": 46326,
            "id": 46326,
            "screenName": "J3D1_WARR10R",
            "lastLogin": 1363336534000,
            "isOnline": "false",
            "avatarImage": "https://api.p04.simcity.com/simcity/rest/user/46326/avatar",
            "cities_count": 0,
            "canChat": "true"
        }
    ]
}

person user1413558    schedule 17.03.2013    source источник
comment
Вы не получаете массив в качестве ответа. у тебя уже есть словарь.   -  person mr.VVoo    schedule 17.03.2013
comment
Могли бы отметить, где происходит сбой, и, возможно, сообщение об ошибке.   -  person Mike D    schedule 17.03.2013
comment
вроде разбирает нормально. в чем проблема   -  person Daij-Djan    schedule 18.03.2013


Ответы (3)


Ваш сервер представляет сертификат, которому ОС не доверяет — мне пришлось использовать флаг -k для curl вашего JSON, поэтому я должен был подумать об этом раньше.

Чтобы обойти это, вам нужно переключиться на использование асинхронного NSURLConnection и реализовать его методы делегата. Посмотрите этот ответ из этот вопрос о переполнении стека, чтобы реализовать рабочее решение вашей проблемы синтаксического анализа. Я реализовал это внутри делегата приложения, но вы можете, например, поместить это в асинхронный NSOperation и использовать его там.

ВНИМАНИЕ! Этот код будет подключаться к любому серверу, независимо от уровня доверия. Это просит человека в средней атаке. Не отправляйте конфиденциальную информацию на сервер, если этот тип доверия включен. Вы ДОЛЖНЫ внедрить код для проверки подлинности вашего сервера и заменить if (YES) в - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge тестом на результат этой проверки.

Насколько я понимаю, это нужно сделать, включив открытый ключ вашего сертификата в пакет приложений в виде файла DER и используя SecCertificateCreateWithData для создания SecCertficiateRef, который будет использоваться в качестве входных данных для SecTrustSetAnchorCertificates. Затем следует использовать SecTrustEvaluate для подтверждения личности. Я основываю это понимание на прочтении Справочник по сертификатам, ключам и службам доверия и пример кода в этой записи блога о том, как доверять собственному сертификату.

@interface AppDelegate () <NSURLConnectionDelegate, NSURLConnectionDataDelegate>

//  This is needed to collect the response as it comes back from the server
@property (nonatomic, strong) NSMutableData *mutableResponseData;

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Xcode template code for setting up window, ignore...
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil];
    } else {
        self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil];
    }
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    //  Instantiate our connection 
    NSString *urlString = @"https://api.p04.simcity.com/simcity/rest/users/search/J3d1.json";
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    if(![NSURLConnection connectionWithRequest:request delegate:self]) {
        NSLog(@"Handle an error case here.");
    }

    return YES;
}


- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //  Prepare to recevie data from the connection
    NSLog(@"did receive response: %@", response);
    self.mutableResponseData = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    //  Handle an error case here
    NSLog(@"did fail with error: %@", error);
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    //  Build up the response data
    [self.mutableResponseData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSError *error = nil;
    id JSONObject = [NSJSONSerialization JSONObjectWithData:self.mutableResponseData options: NSJSONReadingMutableContainers error:&error];
    if (!JSONObject) {
        //  Handle error here
        NSLog(@"error with JSON object");
    }
    else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
        //we're in business
        NSDictionary *dict = JSONObject;
        NSLog(@"dict is %@", dict);
    }
    else {
        //  Handle case of other root JSON object class...
    }
}


//  The following two methods allow any credential to be used. THIS IS VULNERABLE TO MAN IN THE MIDDLE ATTACK IN ITS CURRENT FORM
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // WE ARE POTENTIALLY TRUSTING ANY CERTIFICATE HERE. Replace YES with verification of YOUR server's identity to avoid man in the middle attack.
        //  FOR ALL THAT'S GOOD DON'T SHIP THIS CODE
#warning Seriously, don't ship this.
        if (YES) {
            [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        }
    }

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];   
}

@end
person Carl Veazey    schedule 17.03.2013
comment
Сделал так, все равно вылетает на той же строке - person user1413558; 17.03.2013
comment
Пожалуйста, подробнее о том, как это сделать, так как это может быть проблемой. - person user1413558; 17.03.2013
comment
@user1413558 user1413558 Я пытаюсь понять это сейчас, я не получаю ни данных ответов, ни NSURLResponse, ни ошибок, так что это будет интересно выяснить :) - person Carl Veazey; 17.03.2013
comment
@user1413558 user1413558 Я только что обновил свой ответ реализацией, которая правильно анализирует. Пожалуйста, внимательно прочитайте мои комментарии, так как это доверяет сертификату любого сервера. - person Carl Veazey; 18.03.2013

NSJSONSerialization всегда будет падать, если данные равны нулю. Вам нужно ввести условие, чтобы увидеть, содержит ли этот URL какие-либо данные.

person ChrisH    schedule 17.03.2013
comment
Нет, не будет. Он установит объект ошибки и вернет nil: developer.apple.com/library/ios/#documentation/Foundation/ - person Mike D; 17.03.2013
comment
@MikeD Попробуйте - при передаче nil происходит сбой с исключением недопустимого аргумента. - person Carl Veazey; 18.03.2013

Попробуйте указать URL-адрес в браузере, чтобы убедиться, что он дает вам действительный JSON, а также убедитесь, что анализируемые данные являются массивом или NSDictionary.

person Hossam Ghareeb    schedule 17.03.2013