NSNull 处理 NSManagedObject 属性值

发布于 2025-01-02 18:06:02 字数 1201 浏览 2 评论 0原文

我正在为 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"]< /code> 之前为其创建 NSUInteger 值。而单行解决方案已经消失了。

我尝试创建一个 @try @catch 块,但是一旦捕获第一个值,它就会跳转整个 @try 块,并且下一个属性将被忽略。

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

I'm setting values for properties of my NSManagedObject, these values are coming from a NSDictionary properly serialized from a JSON file. My problem is, that, when some value is [NSNull null], I can't assign directly to the property:

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

this throws a NSInvalidArgumentException

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

I could easily check the value for [NSNull null] and assign nil instead:

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

But I think this is not elegant and gets messy with lots of properties to set.

Also, this gets harder when dealing with NSNumber properties:

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

The NSInvalidArgumentException is now:

[NSNull unsignedIntegerValue]: unrecognized selector sent to instance

In this case I have to treat [dict valueForKey:@"round"] before making an NSUInteger value of it. And the one line solution is gone.

I tried making a @try @catch block, but as soon as the first value is caught, it jumps the whole @try block and the next properties are ignored.

Is there a better way to handle [NSNull null] or perhaps make this entirely different but easier?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

何时共饮酒 2025-01-09 18:06:02

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

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

然后您可以编写类似的内容

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

或者您可以预处理字典并在之前将所有 NSNull 替换为 nil甚至尝试将其填充到您的托管对象中。

It might be a little easier if you wrap this in a macro:

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

Then you can write things like

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

Alternatively you can pre-process your dictionary and replace all NSNulls with nil before even trying to stuff it into your managed object.

瑶笙 2025-01-09 18:06:02

好吧,我今天早上刚醒来就找到了一个很好的解决方案。怎么样:

使用接收可变数组和字典的选项序列化 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[NSNull null] 相反。

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

之前:

// 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

Ok, I've just woke up this morning with a good solution. What about this:

Serialize the JSON using the option to receive Mutable Arrays and Dictionaries:

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

Get a set of keys that have [NSNull null] values from the leafDict:

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

Remove the filtered properties from your Mutable leafDict:

[leafDict removeObjectsForKeys:[nullSet allObjects]];

Now when you call fight.winnerID = [dict objectForKey:@"winner"]; winnerID is automatically going to be (null) or nil as opposed to <null> or [NSNull null].

Not relative to this, but I also noticed that it is better to use a NSNumberFormatter when parsing strings to NSNumber, the way I was doing was getting integerValue from a nil string, this gives me an undesired NSNumber of 0, when I actually wanted it to be nil.

Before:

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

After:

__autoreleasing NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
fight.round = [numberFormatter numberFromString:[leafDict valueForKey:@"round"]];    
// Result: fight.round = nil
清旖 2025-01-09 18:06:02

我编写了几个类别方法,以便在使用之前从 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。

I wrote a couple of category methods to strip nulls from a JSON-generated dictionary or array prior to use:

@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

It would be nice if the standard JSON parsing libs had this behaviour by default - it's almost always preferable to omit null objects than to include them as NSNulls.

南烟 2025-01-09 18:06:02

另一种方法是

-[NSObject setValuesForKeysWithDictionary:]

在这种情况下,您可以

[fight setValuesForKeysWithDictionary:dict];

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

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

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

Another method is

-[NSObject setValuesForKeysWithDictionary:]

In this scenario you could do

[fight setValuesForKeysWithDictionary:dict];

In the header NSKeyValueCoding.h it defines that "Dictionary entries whose values are NSNull result in -setValue:nil forKey:key messages being sent to the receiver.

The only downside is you will have to transform any keys in the dictionary to keys that are in the receiver. i.e.

dict[@"winnerID"] = dict[@"winner"];
[dict removeObjectForKey:@"winner"];
饮湿 2025-01-09 18:06:02

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

为“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"];

就是这样

I was stuck with the same problem, found this post, did it in a slightly different way.Using category only though -

Make a new category file for "NSDictionary" and add this one method -

@implementation NSDictionary (SuperExtras)

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

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

return result;

}

@end

Later on to use it in code, for properties that can have NSNULL in them just use it this way -

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

Thats it

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文