对NSManagedObject属性值进行NSNull处理
我为我的NSManagedObject
属性设置值,这些值来自NSDictionary
正确序列化从JSON文件。 我的问题是,当某些值是[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"]
然后再创build它的NSUInteger
值。 而一线解决scheme不见了。
我尝试了一个@try @catch块,但只要第一个值被捕获,它就会跳转整个@try块,并忽略下一个属性。
有没有更好的方法来处理[NSNull null]
或者使这完全不同,但更容易?
如果你用macros包装它可能会更容易一些:
#define NULL_TO_NIL(obj) ({ __typeof__ (obj) __obj = (obj); __obj == [NSNull null] ? nil : obj; })
那么你可以写一些类似的东西
fight.winnerID = NULL_TO_NIL([dict objectForKey:@"winner"]);
或者,你可以预先处理你的字典,并用nil
replace所有的NSNull
,甚至试图将其填充到pipe理对象中。
好吧,我今天早上刚刚醒来,有一个很好的解决scheme。 那这个呢:
序列化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"];
与<null>
或者[NSNull null]
相比,winnerID会自动变为(null)
或nil
。
不是相对于此,但我也注意到,当parsingstring到NSNumberFormatter
时使用NSNumberFormatter
更好,我做的是从一个零string获取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
我写了几个类别的方法来从JSON生成的字典或数组中去除null之前使用:
@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
如果标准JSONparsing库默认具有这种行为,那将是非常好的 – 忽略null对象比将它们包含为NSNull几乎总是可取的。
另一种方法是
-[NSObject setValuesForKeysWithDictionary:]
在这种情况下,你可以做
[fight setValuesForKeysWithDictionary:dict];
在头文件NSKeyValueCoding.h中,它定义了“值为NSNull的字典条目导致-setValue:nil forKey:key
消息被发送到接收者。
唯一的缺点是你将不得不把字典中的任何键变成接收器中的键。 即
dict[@"winnerID"] = dict[@"winner"]; [dict removeObjectForKey:@"winner"];
我被卡住了同样的问题,发现这个post,做了一个稍微不同的方式。只使用类别 –
为“NSDictionary”创build一个新的分类文件并添加这一个方法 –
@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"];
而已