【问题标题】:NSNull handling for NSManagedObject properties valuesNSManagedObject 属性值的 NSNull 处理
【发布时间】:2012-02-04 02:28:24
【问题描述】:

我正在为 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],或者可能使这完全不同但更容易?

【问题讨论】:

  • 出于好奇,在数字的情况下,您为什么要解开并重新包装数字? fight.round = [dict objectForKey:@"round"] 怎么了?
  • @KevinBallard 不可接受的属性值类型:property = "round";所需类型 = NSNumber;给定类型 = __NSCFString;值 = 3。
  • 如果是NSString,那么-unsignedIntegerValue 将不起作用,因为NSString 上没有定义该方法。您可以改用-integerValue,它在NSNumberNSString 上都定义了。

标签: objective-c json parsing nsmanagedobject nsnull


【解决方案1】:

如果你把它包装在一个宏中可能会更容易一些:

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

然后你可以写类似的东西

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

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

【讨论】:

  • 谢谢,这很有用!您是否推荐一个好的资源来了解有关宏的更多信息?
  • @Lucien:我希望我做到了,但没有。不过那里有很多信息,所以只需谷歌即可。
  • @Kevin Ballard .... 真的很棒 ..... 非常有帮助 ..... 你是天才 ... 快乐编码!!!!!!
  • 我怎样才能快速做到这一点?
  • 找到这个答案 func nullToNil(value : Any?) -> Any? { if value is NSNull { return nil } else { return value } } people.age = nullToNil(peopleDict["age"])
【解决方案2】:

好的,我今天早上刚醒来,找到了一个很好的解决方案。这个呢:

使用接收可变数组和字典的选项序列化 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,而不是 &lt;null&gt;[NSNull null]

与此无关,但我也注意到在将字符串解析为 NSNumber 时最好使用NSNumberFormatter,我所做的方式是从 nil 字符串中获取integerValue,这给了我一个不想要的 NSNumber 0,当我实际上希望它为零时。

之前:

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

之后:

__autoreleasing NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
fight.round = [numberFormatter numberFromString:[leafDict valueForKey:@"round"]];    
// Result: fight.round = nil

【讨论】:

    【解决方案3】:

    我写了几个类别方法来在使用前从 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 解析库默认具有这种行为,那就太好了 - 忽略空对象几乎总是比将它们包含为 NSNull 更可取。

    【讨论】:

      【解决方案4】:

      另一种方法是

      -[NSObject setValuesForKeysWithDictionary:]
      

      在这种情况下你可以这样做

      [fight setValuesForKeysWithDictionary:dict];
      

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

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

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

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题,找到了这篇文章,但方式略有不同。但仅使用类别 -

        为“NSDictionary”创建一个新的类别文件并添加这个方法-

        @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"];
        

        就是这样

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多