NSNull handling for NSManagedObject properties values

后端 未结 5 608
说谎
说谎 2021-01-30 09:07

I\'m setting values for properties of my NSManagedObject, these values are coming from a NSDictionary properly serialized from a JSON file. My problem

相关标签:
5条回答
  • 2021-01-30 09:32

    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
    
    0 讨论(0)
  • 2021-01-30 09:39

    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.

    0 讨论(0)
  • 2021-01-30 09:43

    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"];
    
    0 讨论(0)
  • 2021-01-30 09:51

    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.

    0 讨论(0)
  • 2021-01-30 09:52

    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

    0 讨论(0)
提交回复
热议问题