0

If got a NSMutableDictionary from Json Data

  NSMutableDictionary *returnedDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

I know that this key returnedDict[@"data"][@"weather"][day][@"tides"] is NSNull in some cases. So I get -[NSNull objectForKeyedSubscript:]

So I try according to this answer How to check if an NSDictionary or NSMutableDictionary contains a key? to check if it is nil or not.

if (returnedDict[@"data"][@"weather"][day][@"tides"]){ some code }

and

if (returnedDict[@"data"][@"weather"][day][@"tides"]!=[NSNull null]){ some code}

does not avoid to run {some code}

How do I check this in the right way?

Community
  • 1
  • 1
  • key is null or value? – Anoop Vaidya Dec 29 '15 at 15:03
  • In general, its wise to have whatever is in the if-clause be something that evaluates to a real BOOL. Using side-effects of the language (in this case 'any non-zero value is considered YES') is less advisasable, because it might cause unexpected false-positives (like here in your case), it makes your code slightly less clear for others and your future self. Also, with Swift in the world now, it's smart to make this a habbit, as in Swift its required that any if-clause evaluates to a Bool. – Joride Dec 29 '15 at 15:16
  • The Value is an instance of NSNull – Wolfgang Giesel Dec 29 '15 at 21:18

3 Answers3

2

So your issue is:

Your server may return null to indicate that an object isn't present. NSJSONSerialization will convert that null into an instance of NSNull. In theory that means that instead of doing result[a][b][c] you need to check whether result[a] is a dictionary and, if so, whether result[a][b] is a dictionary, etc, etc, which is repetitious and error-prone?

Perhaps the easiest thing might be to remove from the dictionary any key with a value of NSNull, so that next time you ask for the value you'll get an ordinary nil, which is safe to message per the usual compound-messaging rules?

NSJSONSerialization won't do that for you but it's easy enough to add after the fact:

@interface NSDictionary(RemoveNullValues)
- (NSDictionary *)ty_collectionWithoutNullValues;
@end

@interface NSArray(RemoveNullValues)
- (NSArray *)ty_collectionWithoutNullValues;
@end

[...]

@implementation NSDictionary(RemoveNullValues)

- (NSDictionary *)ty_collectionWithoutNullValues {

    NSMutableDictionary *reducedDictionary = [self mutableCopy];

    // remove any keys for which NSNull is the direct value
    NSArray *keysEvaluatingToNull = [self allKeysForObject:[NSNull null]];
    [reducedDictionary removeObjectsForKeys:keysEvaluatingToNull];

    // ask any child dictionaries to do the same; note that it's safe
    // to mutate reducedDictionary in this array because allKeys is a 
    // copy property; what you're iterating is not reducedDictionary 
    // but a snapshot of its keys when the array first began
    for (id key in [reducedDictionary allKeys]) {
        id child = reducedDictionary[key];
        if ([child respondsToSelector:@selector(ty_collectionWithoutNullValues)]) {
            reducedDictionary[key] = [child ty_collectionWithoutNullValues];
        }
    }

    return [reducedDictionary copy];
}

@end

@implementation NSArray(RemoveNullValues)
- (NSArray *)ty_collectionWithoutNullValues {
    NSMutableArray *reducedArray = [NSMutableArray array];

    for (id child in self) {
        if ([child isKindOfClass:[NSNull class]]) continue;

        if ([child respondsToSelector:@selector(ty_collectionWithoutNullValues)]) {
            [reducedArray addObject:[child ty_collectionWithoutNullValues]];
        } else {
            [reducedArray addObject:child];
        }
   }

   return [reducedArray copy];
}
@end
Tommy
  • 99,986
  • 12
  • 185
  • 204
  • This solution seems to be the best, but for now I am not able to make it work in my code. – Wolfgang Giesel Dec 29 '15 at 21:58
  • 1
    @WolfgangGiesel - If you have dictionaries inside of arrays, in any combination, which is not uncommon in JSON? If so you'll need to walk the whole JSON structure to look for the dictionaries and then apply this answer to what you find. – CRD Dec 29 '15 at 22:24
  • Yes, apologies, written extemporaneously: assumes a top level dictionary with only children that are dictionaries. It really needs expanding to handle the `NSArray` case. I'll try to edit later; marking as community wiki for now in case somebody else wants to step in. – Tommy Dec 29 '15 at 22:26
  • Updated with an attempt to filter arrays too. – Tommy Dec 30 '15 at 15:55
0

You must read this answer in conjunction with the accepted answer and comments to the question Is there NSMutableDictionary literal syntax to remove an element?

Following on from the linked answer you can quietly remove all the NSNull's and return nil instead if you access the element using the literal syntax (i.e. not using objectForKey:) by adding the following to your application:

@implementation NSDictionary (ClobberNSNull)

- (id) objectForKeyedSubscript:(id<NSCopying>)key
{
   id result = [self objectForKey:key];
   return result == NSNull.null ? nil : result;
}

@end

Now when you use the syntax:

dictionary[key]

if the matching object is NSNull then nil will be returned just as if the key did not exist.

There are caveats, see the linked question, and you need to decide if this approach is suitable for your situation. But it is simple.

HTH

Note: Before someone comments, NSNull is a singleton so the == is OK.

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
  • So it appears it (a) works (b) solves the problem (c) follows an Apple pattern and (d) contains *caveat* and it earns a silent down vote? Harsh. – CRD Dec 29 '15 at 22:18
  • Disclaimer: the negative vote isn't from me. That being said: maybe the caveat that this will affect all dictionaries offended? – Tommy Dec 29 '15 at 22:24
  • @Tommy - Well that is a caveat (though probably not a significant one), and maybe you're right it is what offended. Who knows? – CRD Dec 29 '15 at 22:45
-1

use if(![returnedDict[@"data"][@"weather"][day][@"tides"] isKindOfClass:[NSNull class]]) { some code }

Sumeet
  • 1,055
  • 8
  • 18
  • 1
    This will only work if the last value is `NSNull`, if any of the other ones are `NSNull` then the application will crash. – Cristik Dec 29 '15 at 19:28