35 votes

Gestion de NSNull pour les valeurs des propriétés NSManagedObject

Je suis à la définition des valeurs pour les propriétés de mon NSManagedObject, ces valeurs sont à venir à partir d'un NSDictionary correctement sérialisé à partir d'un fichier JSON. Mon problème, c'est que, lorsque la valeur est [NSNull null], je ne peux pas attribuer directement à la propriété:

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

cela jette un NSInvalidArgumentException

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

Je pourrais facilement vérifier la valeur de [NSNull null] et attribuer nil à la place:

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

Mais je pense que ce n'est pas élégant et devient malpropre avec beaucoup de propriétés à définir.

Aussi, cela devient plus difficile lorsque vous traitez avec des NSNumber propriétés:

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

L' NSInvalidArgumentException est maintenant:

[NSNull unsignedIntegerValue]: unrecognized selector sent to instance

Dans ce cas, j'ai pour traiter l' [dict valueForKey:@"round"] avant de faire un NSUInteger de la valeur de cela. Et la solution est allé.

J'ai essayé de faire un @essayez @bloc catch, mais dès que la première valeur est attrapé, il saute à l'ensemble de l' @bloc try et la prochaine propriétés sont ignorées.

Est-il une meilleure façon de gérer [NSNull null] ou peut-être prendre cela totalement différent, mais plus facile?

65voto

Kevin Ballard Points 88866

Cela pourrait être un peu plus facile si vous intégrez ceci dans une macro:

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

Ensuite, vous pouvez écrire des choses comme

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

Vous pouvez également pré-traiter votre dictionnaire et remplacer tous les NSNull s par nil avant même d'essayer de l'intégrer à votre objet géré.

8voto

Lucien Points 942

Ok, je viens de me suis réveillé ce matin avec une bonne solution. Ce:

Sérialiser le JSON à l'aide de l'option de recevoir Mutable Tableaux et des Dictionnaires:

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

Obtenir un ensemble de touches qui ont [NSNull null] des valeurs de la leafDict:

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

Supprimer le filtre propriétés à partir de votre Mutable leafDict:

[leafDict removeObjectsForKeys:[nullSet allObjects]];

Maintenant, lorsque vous appelez fight.winnerID = [dict objectForKey:@"winner"]; winnerID est automatiquement va être (null) ou nil plutôt <null> ou [NSNull null].

Pas par rapport à cela, mais j'ai aussi remarqué qu'il est préférable d'utiliser un NSNumberFormatter lors de l'analyse des chaînes de NSNumber, la façon dont je le faisais a été prise en integerValue à partir d'un néant de la chaîne, cela me donne un indésirable NSNumber d' 0, quand en fait, je voulais qu'il soit nul.

Avant:

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

Après:

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

1voto

Nick Lockwood Points 23277

J'ai écrit quelques méthodes de catégorie pour supprimer les valeurs NULL d'un dictionnaire ou d'un tableau généré par JSON avant de les utiliser:

 @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
 

Ce serait bien si les bibliothèques d'analyse syntaxique JSON standard avaient ce comportement par défaut - il est presque toujours préférable d'omettre les objets null que de les inclure en tant que NSNulls.

0voto

dmpontifex Points 346

Une autre méthode est

 -[NSObject setValuesForKeysWithDictionary:]
 

Dans ce scénario, vous pourriez faire

 [fight setValuesForKeysWithDictionary:dict];
 

Dans l'en-tête NSKeyValueCoding.h, il est défini que "Les entrées de dictionnaire dont les valeurs sont NSNull entraînent l'envoi de messages -setValue:nil forKey:key au destinataire.

Le seul inconvénient est que vous devrez transformer les clés du dictionnaire en clés qui se trouvent dans le récepteur. c'est à dire

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X