NSNull处理NSManagedObject属性值

时间:2012-02-04 02:28:24

标签: objective-c json parsing nsmanagedobject nsnull

我正在为NSManagedObject的属性设置值,这些值来自JSON文件中正确序列化的NSDictionary。我的问题是,当某个值为[NSNull null]时,我无法直接分配给该属性:

    fight.winnerID = [dict objectForKey:@"winner"];

这会抛出NSInvalidArgumentException

"winnerID"; desired type = NSString; given type = NSNull; value = <null>;

我可以轻松检查[NSNull null]的值并改为分配nil

fight.winnerID = [dict objectForKey:@"winner"] == [NSNull null] ? nil : [dict objectForKey:@"winner"];

但我认为这不是很优雅,而且要设置很多属性会变得混乱。

此外,在处理NSNumber属性时会变得更难:

fight.round = [NSNumber numberWithUnsignedInteger:[[dict valueForKey:@"round"] unsignedIntegerValue]]

NSInvalidArgumentException现在是:

[NSNull unsignedIntegerValue]: unrecognized selector sent to instance

在这种情况下,我必须在[dict valueForKey:@"round"]之前处理NSUInteger。一线解决方案消失了。

我尝试创建一个@try @catch块,但只要捕获到第一个值,它就会跳转整个@try块,并忽略下一个属性。

有没有更好的方法来处理[NSNull null]或者可能使这完全不同但更容易?

5 个答案:

答案 0 :(得分:65)

如果将它包装在宏中,可能会更容易一些:

#define NULL_TO_NIL(obj) ({ __typeof__ (obj) __obj = (obj); __obj == [NSNull null] ? nil : obj; })

然后你可以写像

这样的东西
fight.winnerID = NULL_TO_NIL([dict objectForKey:@"winner"]);

或者,您可以预先处理字典,并将所有NSNull替换为nil,然后再尝试将其填充到您的托管对象中。

答案 1 :(得分:8)

好的,我今天早上刚刚醒来时提出了一个很好的解决方案。那怎么样:

使用接收Mutable Arrays和Dictionaries的选项序列化JSON:

NSMutableDictionary *rootDict = [NSJSONSerialization JSONObjectWithData:_receivedData options:NSJSONReadingMutableContainers error:&error];
...

从leafDict获取一组具有[NSNull null]值的键:

NSSet *nullSet = [leafDict keysOfEntriesWithOptions:NSEnumerationConcurrent passingTest:^BOOL(id key, id obj, BOOL *stop) {
    return [obj isEqual:[NSNull null]] ? YES : NO;
}];

从Mutable leafDict中删除过滤后的属性:

[leafDict removeObjectsForKeys:[nullSet allObjects]];

现在,当您致电fight.winnerID = [dict objectForKey:@"winner"];时,winnerID将自动变为(null)nil,而不是<null>[NSNull null]

与此不相关,但我也注意到在将字符串解析为NSNumber时最好使用NSNumberFormatter,我的方式是从nil字符串中获取integerValue,这给了我<{1}}的一个不受欢迎的NSNumber,当我真的希望它为零时。

在:

0

后:

// when [leafDict valueForKey:@"round"] == nil
fight.round = [NSNumber numberWithInteger:[[leafDict valueForKey:@"round"] integerValue]]
// Result: fight.round = 0

答案 2 :(得分:1)

我写了几个类别方法,在使用之前从JSON生成的字典或数组中删除空值:

@implementation NSMutableArray (StripNulls)

- (void)stripNullValues
{
    for (int i = [self count] - 1; i >= 0; i--)
    {
        id value = [self objectAtIndex:i];
        if (value == [NSNull null])
        {
            [self removeObjectAtIndex:i];
        }
        else if ([value isKindOfClass:[NSArray class]] ||
                 [value isKindOfClass:[NSDictionary class]])
        {
            if (![value respondsToSelector:@selector(setObject:forKey:)] &&
                ![value respondsToSelector:@selector(addObject:)])
            {
                value = [value mutableCopy];
                [self replaceObjectAtIndex:i withObject:value];
            }
            [value stripNullValues];
        }
    }
}

@end


@implementation NSMutableDictionary (StripNulls)

- (void)stripNullValues
{
    for (NSString *key in [self allKeys])
    {
        id value = [self objectForKey:key];
        if (value == [NSNull null])
        {
            [self removeObjectForKey:key];
        }
        else if ([value isKindOfClass:[NSArray class]] ||
                 [value isKindOfClass:[NSDictionary class]])
        {
            if (![value respondsToSelector:@selector(setObject:forKey:)] &&
                ![value respondsToSelector:@selector(addObject:)])
            {
                value = [value mutableCopy];
                [self setObject:value forKey:key];
            }
            [value stripNullValues];
        }
    }
}

@end

如果默认情况下标准JSON解析库有这种行为会很好 - 省略空对象几乎总是比将它们包含为NSNulls更好。

答案 3 :(得分:0)

另一种方法是

-[NSObject setValuesForKeysWithDictionary:]

在这种情况下你可以做到

[fight setValuesForKeysWithDictionary:dict];

在标题NSKeyValueCoding.h中,它定义“值为NSNull的字典条目导致-setValue:nil forKey:key个消息被发送到接收方。

唯一的缺点是你必须将字典中的任何键转换为接收器中的键。即。

dict[@"winnerID"] = dict[@"winner"];
[dict removeObjectForKey:@"winner"];

答案 4 :(得分:0)

我遇到了同样的问题,发现这篇文章,以稍微不同的方式做到了。仅使用类别 -

为&#34; NSDictionary&#34;创建一个新的类别文件并添加这一个方法 -

@implementation NSDictionary (SuperExtras)

- (id)objectForKey_NoNSNULL:(id)aKey
{
    id result = [self objectForKey:aKey];

if(result==[NSNull null])
{
    return nil;
}

return result;

}

@end

稍后在代码中使用它,对于可以在其中使用NSNULL的属性,只需使用它 -

newUser.email = [loopdict objectForKey_NoNSNULL:@"email"];

多数民众赞成