【问题标题】:NSNull length error when parsing JSON to Table View将 JSON 解析为表视图时出现 NSNull 长度错误
【发布时间】:2013-11-07 18:03:56
【问题描述】:

我正在尝试将 JSON 文件解析为我的 iOS 应用程序表视图。 当我启动应用程序时,我看到它解析数据,但是当我开始滚动应用程序时立即崩溃并给我这个错误:Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull length]: unrecognized selector sent to instance 0x38094a60'

我的代码:

#import "FirstViewController.h"
#import "YoutubePost.h"

@interface FirstViewController ()
{
    NSInteger refreshIndex;
    NSArray *title;
    NSArray *about;
    NSArray *views;
    NSArray *rating;
    NSArray *votes;
    NSArray *content;
}
@end

@implementation FirstViewController
@synthesize tweets;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.title = NSLocalizedString(@"Videos", @"Videos");
        self.tabBarItem.image = [UIImage imageNamed:@"newtab1"];
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.myTableView.separatorColor = [UIColor clearColor];

    [self issueLoadRequest];

    [self setNeedsStatusBarAppearanceUpdate];
}

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleLightContent;
}

#pragma mark - Table view data source

- (void)issueLoadRequest
{
    // Dispatch this block asynchronosly. The block gets JSON data from the specified URL and performs the proper selector when done.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"my-site.php/file.json"]];
        [self performSelectorOnMainThread:@selector(receiveData:) withObject:data waitUntilDone:YES];
    });
}

- (void)receiveData:(NSData *)data {
    // When we have the data, we serialize it into native cocoa objects. (The outermost element from twitter is
    // going to be an array. I JUST KNOW THIS. Reload the tableview once we have the data.
    self.tweets = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    [self.myTableView reloadData];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.tweets.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *simpleTableIdentifier = @"YoutubePost";

    YoutubePost *cell = (YoutubePost *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil)
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"YoutubePost" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }

    // The element in the array is going to be a dictionary. I JUST KNOW THIS. The key for the tweet is "text".
    NSDictionary *temp = [self.tweets objectAtIndex:indexPath.row];
    NSDictionary *tweet = [self nullFreeDictionaryWithDictionary:temp];

    cell.title.text = [tweet objectForKey:@"title"];
    cell.views.text = [tweet objectForKey:@"views"];
    return cell;
}

- (NSDictionary *)nullFreeDictionaryWithDictionary:(NSDictionary *)dictionary
{
    NSMutableDictionary *replaced = [NSMutableDictionary dictionaryWithDictionary:dictionary];
    // Iterate through each key-object pair.
    [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
        // If object is a dictionary, recursively remove NSNull from dictionary.
        if ([object isKindOfClass:[NSDictionary class]]) {
            NSDictionary *innerDict = object;
            replaced[key] = [NSDictionary nullFreeDictionaryWithDictionary:innerDict];
        }
        // If object is an array, enumerate through array.
        else if ([object isKindOfClass:[NSArray class]]) {
            NSMutableArray *nullFreeRecords = [NSMutableArray array];
            for (id record in object) {
                // If object is a dictionary, recursively remove NSNull from dictionary.
                if ([record isKindOfClass:[NSDictionary class]]) {
                    NSDictionary *nullFreeRecord = [NSDictionary nullFreeDictionaryWithDictionary:record];
                    [nullFreeRecords addObject:nullFreeRecord];
                }
                else {
                    if (object == [NSNull null]) {
                        [nullFreeRecords addObject:@""];
                    }
                    else {
                        [nullFreeRecords addObject:record];
                    }
                }
            }
            replaced[key] = nullFreeRecords;
        }
        else {
            // Replace [NSNull null] with nil string "" to avoid having to perform null comparisons while parsing.
            if (object == [NSNull null]) {
                replaced[key] = @"";
            }
        }
    }];

    return [NSDictionary dictionaryWithDictionary:replaced];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 397;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];

    NSString * storyLink = [[tweets objectAtIndex: storyIndex] objectForKey:@"link"];

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:storyLink]];
    // Spit out some pretty JSON for the tweet that was tapped. Neato.
    NSString *formattedJSON = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:[self.tweets objectAtIndex:indexPath.row] options:NSJSONWritingPrettyPrinted error:nil] encoding:NSUTF8StringEncoding];
    NSLog(@"tweet:\n%@", formattedJSON);
}


@end

在我安装新的 Xcode 5 之前,我没有收到此错误。有人可以帮我吗?

谢谢。

【问题讨论】:

  • NSNull 不支持“长度”方法。您的数据中有一个 NSNull,表示作为“null”传输的 JSON 元素。 (如果您查看,该消息会为您提供一个行号,以准确告诉您是哪个引用遇到了问题。然后您只需添加逻辑来检测 NSNull 并处理这种情况。)
  • 对不起,我只是个初学者。我究竟应该怎么做? @HotLicks
  • 您可以通过检查 [object isKindOfClass [NSNull class]] 来检查对象是否为 NSNull。
  • 至少,将完整的异常消息(包括行号)复制/粘贴到您的问题中,然后确保您列出的代码包含该行号。这是调试所需的最基本的东西。
  • 对我来说,这些不显示行号......太糟糕了!

标签: ios json xcode uitableview


【解决方案1】:

在解析 JSON 结果时,我自己一直在做的就是用空字符串 (@"") 替换 NSNull 的每个实例,使用以下方法:

+ (NSDictionary *)nullFreeDictionaryWithDictionary:(NSDictionary *)dictionary
{
    NSMutableDictionary *replaced = [NSMutableDictionary dictionaryWithDictionary:dictionary];
    // Iterate through each key-object pair.
    [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
        // If object is a dictionary, recursively remove NSNull from dictionary.
        if ([object isKindOfClass:[NSDictionary class]]) {
            NSDictionary *innerDict = object;
            replaced[key] = [NSDictionary nullFreeDictionaryWithDictionary:innerDict];
        }
        // If object is an array, enumerate through array.
        else if ([object isKindOfClass:[NSArray class]]) {
            NSMutableArray *nullFreeRecords = [NSMutableArray array];
            for (id record in object) {
                // If object is a dictionary, recursively remove NSNull from dictionary.
                if ([record isKindOfClass:[NSDictionary class]]) {
                    NSDictionary *nullFreeRecord = [NSDictionary nullFreeDictionaryWithDictionary:record];
                    [nullFreeRecords addObject:nullFreeRecord];
                }
                else {
                    if (object == [NSNull null]) {
                        [nullFreeRecords addObject:@""];
                    }
                    else {
                        [nullFreeRecords addObject:record];
                    }
                }
            }
            replaced[key] = nullFreeRecords;
        }
        else {
            // Replace [NSNull null] with nil string "" to avoid having to perform null comparisons while parsing.
            if (object == [NSNull null]) {
                replaced[key] = @"";
            }
        }
    }];

    return [NSDictionary dictionaryWithDictionary:replaced];
}

当然,这依赖于作为字典的 JSON 返回格式,但如果您将所有参数类型替换为 id 并执行类检查,您可以轻松修改它以适应其他数据类型。

--编辑-- 如果这是您将使用 JSON 的唯一地方,请更改

NSDictionary *tweet = [self.tweets objectAtIndex:indexPath.row]; 

NSDictionary *temp = [self.tweets objectAtIndex:indexPath.row];
NSDictionary *tweet = [NSDictionary nullFreeDictionaryWithDictionary:temp];

请注意,我将 nullFreeDictionaryWithDictionary 作为 NSDictionary 类的 Objective-C 类别。如果您不打算在其他任何地方使用此方法,您可能只需将其添加到视图控制器的实现文件中即可。

【讨论】:

  • 好的,太好了。谢谢。我是初学者,那么我该如何实现呢? @Kamaros
  • 所以我应该创建这个: NSDictionary *nullFreeDictionaryWithDictionary;在标题中然后综合它是主要的吗?它不起作用,它只是说:没有已知的选择器'nullFreeDictionaryWithDictionary:'的类方法:@Kamaros
  • 那是因为我把它写成一个类别,让它更可重用。在您的情况下,将减号更改为加号,然后调用 NSDictionary *tweet = [self nullFreeDictionaryWithDictionary:temp]; 而不是 NSDictionary *tweet = [NSDictionary nullFreeDictionaryWithDictionary:temp];
  • 我已将减号更改为加号,但它给了我:No known class method for selector 'nullFreeDictionaryWithDictionary:... @Kamaros
  • 呃,我的意思是把加号改成减号。我的错。
猜你喜欢
  • 1970-01-01
  • 2014-09-09
  • 2016-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多