从特定 url 提取数据时 NSJSONSerialization 崩溃

Posted

技术标签:

【中文标题】从特定 url 提取数据时 NSJSONSerialization 崩溃【英文标题】:NSJSONSerialization crashes when pulling data from specific url 【发布时间】:2013-03-17 19:26:51 【问题描述】:

我正在尝试从此网址提取数据,但遇到了一些问题。为什么这会在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"
        
    ]

【问题讨论】:

你没有得到一个数组作为响应。你已经有了一本字典。 可以标记崩溃的地方,可能还有错误信息。 它似乎解析得很好。有什么问题 【参考方案1】:

您的服务器提供了一个不受操作系统信任的证书 - 我必须使用 -k 标志来标记您的 JSON,所以应该早点想到这一点。

为了解决这个问题,您需要切换到使用异步NSURLConnection 并实现其委托方法。请参阅this stack overflow question 的此答案,以实施针对您的解析问题的有效解决方案。我在应用程序委托中实现了它,但是您可以将它放在异步 NSOperation 中并在那里使用它。

警告:此代码将连接到任何服务器,无论信任如何。这是要求一个人在中间攻击。启用此类信任时,请勿将敏感信息发送到服务器。您必须实现代码来验证您的服务器的身份,并将- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 中的if (YES) 替换为针对该检查结果的测试。

我目前的理解是,这应该通过将您的证书的公钥作为 DER 文件包含在应用程序包中来完成,并使用SecCertificateCreateWithData 创建一个SecCertficiateRef,该SecCertficiateRef 将用作SecTrustSetAnchorCertificates 的输入。然后,应该使用SecTrustEvaluate 来验证身份。我基于阅读Certificate, Key, and Trust Services Reference 和this blog post 中的示例代码了解如何信任您自己的证书。

@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

【讨论】:

这样做了,仍然在同一行崩溃 请详细了解如何执行此操作,因为这听起来可能是问题所在。 @user1413558 我现在想弄清楚,我没有得到响应数据,没有 NSURLResponse,也没有错误,所以这很有趣:) @user1413558 我刚刚用正确解析的实现更新了我的答案。请仔细阅读我的 cmets,因为它信任任何服务器的证书。【参考方案2】:

如果数据为零,NSJSONSerialization 总是会崩溃。您需要输入条件以查看该 URL 是否包含任何数据。

【讨论】:

不,不会的。它将设置错误对象并返回nil:developer.apple.com/library/ios/#documentation/Foundation/… @MikeD 试试看 - 传递 nil 会因参数无效异常而崩溃。【参考方案3】:

尝试将 url 放入浏览器以确保为您提供有效的 JSON,同时确保解析的数据是 Array 或 NSDictionary

【讨论】:

以上是关于从特定 url 提取数据时 NSJSONSerialization 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

使用python从json中搜索和提取特定数据

从特定 url 抓取链接

快速从特定 URL 获取 JSON 数据

在名称未知时从注册表项中提取特定数据 (USBSTOR)

从 NSPersistentStoreCoordinator 提取特定更新

如何通过使用 node.js 从实时 json 数据中提取特定字段来创建新的单个 json 对象