NSNull handling for NSManagedObject properties values

匿名 (未验证) 提交于 2019-12-03 02:44:02

问题:

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?

回答1:

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.



回答2:

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 


回答3:

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.



回答4:

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


回答5:

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



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!