将JSON解析为表视图时出现NSNull长度错误

时间:2013-10-28 19:45:25

标签: ios json xcode uitableview

我正在尝试将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之前,我没有收到此错误。有人能帮助我吗?

感谢。

1 个答案:

答案 0 :(得分:8)

在解析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替换所有参数类型并执行类检查,则可以轻松修改它以适应其他数据类型。

- Edit-- 如果这是您将使用JSON的唯一地方,那么请更改

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

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

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