Replace all NSNull objects in an NSDictionary
Asked Answered
C

8

13

I'm curious, I currently have an NSDictionary where some values are set to an NSNull object thanks to the help of json-framework.

The aim is to strip all NSNull values and replace it with an empty string.

I'm sure someone has done this somewhere? No doubt it is probably a four liner and is simple, I am just far too burnt out to figure this out on my own.

Crib answered 10/11, 2011 at 4:52 Comment(2)
See my answer here: #21922526Calabresi
I made a category which does that. You can find it here github.com/bismasaeed00/NullReplacerPayload
A
16

Really simple:

@interface NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings;
@end

@implementation NSDictionary (JRAdditions)

- (NSDictionary *)dictionaryByReplacingNullsWithStrings {
   const NSMutableDictionary *replaced = [self mutableCopy];
   const id nul = [NSNull null];
   const NSString *blank = @"";

   for(NSString *key in self) {
      const id object = [self objectForKey:key];
      if(object == nul) {
         //pointer comparison is way faster than -isKindOfClass:
         //since [NSNull null] is a singleton, they'll all point to the same
         //location in memory.
         [replaced setObject:blank 
                      forKey:key];
      }
   }

   return [replaced copy];
}

@end

Usage:

NSDictionary *someDictThatHasNulls = ...;
NSDictionary *replacedDict = [someDictThatHasNulls dictionaryByReplacingNullsWithStrings];
Ameliaamelie answered 10/11, 2011 at 5:0 Comment(4)
Or, since NSNull is a singleton, you can test value pointers directly: if([self objectForKey:key]==[NSNull null]).Parfait
Yes.. But what if dictionary contains NSArray of NULL's? Or other NSDictionary with NULL's values? Be carefully to use this! I highly recommend to use Stakenborg's modification of this method, described below!Mishnah
Use Stakenborg's method below!Reconnoitre
Where do we copy and paste this to? This gives me errors when I try it and paste to file I want it in below, sorry im new to iOSBrummett
F
44

I've made a few changes to Jacob's original answer to extend it to handle dictionaries and arrays stored within the original dictionary.

#import "NSDictionary+NullReplacement.h"
#import "NSArray+NullReplacement.h"

@implementation NSDictionary (NullReplacement)

- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
    const NSMutableDictionary *replaced = [self mutableCopy];
    const id nul = [NSNull null];
    const NSString *blank = @"";
    
    for (NSString *key in self) {
        id object = [self objectForKey:key];
        if (object == nul) [replaced setObject:blank forKey:key];
        else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
        else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
    }
    return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}

@end

And there's also an array category of course:

#import "NSArray+NullReplacement.h"
#import "NSDictionary+NullReplacement.h"

@implementation NSArray (NullReplacement)

- (NSArray *)arrayByReplacingNullsWithBlanks  {
    NSMutableArray *replaced = [self mutableCopy];
    const id nul = [NSNull null];
    const NSString *blank = @"";
    for (int idx = 0; idx < [replaced count]; idx++) {
        id object = [replaced objectAtIndex:idx];
        if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
        else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
        else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
    }
    return [replaced copy];
}

@end

With this, you can take any array or dictionary and recursively wipe out all the [NSNull null] instances.

P.S. For completion's sake, here are the header files:

@interface NSDictionary (NullReplacement)

- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;

@end

And the array header:

@interface NSArray (NullReplacement)

- (NSArray *)arrayByReplacingNullsWithBlanks;

@end
Flowery answered 22/5, 2013 at 21:42 Comment(1)
I made a category which does that. You can find it here github.com/bismasaeed00/NullReplacerPayload
A
16

Really simple:

@interface NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings;
@end

@implementation NSDictionary (JRAdditions)

- (NSDictionary *)dictionaryByReplacingNullsWithStrings {
   const NSMutableDictionary *replaced = [self mutableCopy];
   const id nul = [NSNull null];
   const NSString *blank = @"";

   for(NSString *key in self) {
      const id object = [self objectForKey:key];
      if(object == nul) {
         //pointer comparison is way faster than -isKindOfClass:
         //since [NSNull null] is a singleton, they'll all point to the same
         //location in memory.
         [replaced setObject:blank 
                      forKey:key];
      }
   }

   return [replaced copy];
}

@end

Usage:

NSDictionary *someDictThatHasNulls = ...;
NSDictionary *replacedDict = [someDictThatHasNulls dictionaryByReplacingNullsWithStrings];
Ameliaamelie answered 10/11, 2011 at 5:0 Comment(4)
Or, since NSNull is a singleton, you can test value pointers directly: if([self objectForKey:key]==[NSNull null]).Parfait
Yes.. But what if dictionary contains NSArray of NULL's? Or other NSDictionary with NULL's values? Be carefully to use this! I highly recommend to use Stakenborg's modification of this method, described below!Mishnah
Use Stakenborg's method below!Reconnoitre
Where do we copy and paste this to? This gives me errors when I try it and paste to file I want it in below, sorry im new to iOSBrummett
B
6

Rolling through the dictionary hunting for NSNull is one way to tackle the problem, but I took a slightly lazier approach. Instead of nil you could assign an empty string, but the principle is the same.

CPJSONDictionary.h

@interface NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey;
@end

CPJSONDictionary.m

@implementation NSDictionary (CPJSONDictionary)

- (id)jsonObjectForKey:(id)aKey {
    id object = [self objectForKey:aKey];
    if ([object isKindOfClass:[NSNull class]]) {
        object = nil;
    }

    return object;
}

@end
Bruyn answered 29/11, 2012 at 11:57 Comment(0)
T
4

I have tested Stakenborg solution. It works well, but it has following problem. If some object is expected to be number, for instance, converting it to NSNull can be a source of error. I have create a new method to directly remove the NSNull entries. This way you only have to check that correspondant key exists.

Add in NSDictionary+NullReplacement

- (NSDictionary *)dictionaryByRemovingNulls{
    const NSMutableDictionary *replaced = [self mutableCopy];
    const id nul = [NSNull null];
    
    for (NSString *key in self) {
        id object = [self objectForKey:key];
        if (object == nul) [replaced removeObjectForKey:key];
        
        else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByRemovingNulls] forKey:key];
        else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByRemovingNulls] forKey:key];
    }
    return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}

And in NSArray+NullReplacement

- (NSArray *)arrayByRemovingNulls {
    NSMutableArray *replaced = [self mutableCopy];
    const id nul = [NSNull null];

    for (int idx = [replaced count]-1; idx >=0; idx--) {
        id object = [replaced objectAtIndex:idx];
        if (object == nul) [replaced removeObjectAtIndex:idx];
        else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByRemovingNulls]];
        else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByRemovingNulls]];
    }
    return [replaced copy];
}
Tracay answered 14/4, 2014 at 11:11 Comment(0)
S
3

another variation:

NSDictionary * NewDictionaryReplacingNSNullWithEmptyNSString(NSDictionary * dict) {
    NSMutableDictionary * const m = [dict mutableCopy];    
    NSString * const empty = @"";
    id const nul = [NSNull null];
    NSArray * const keys = [m allKeys];
    for (NSUInteger idx = 0, count = [keys count]; idx < count; ++idx) {
        id const key = [keys objectAtIndex:idx];
        id const obj = [m objectForKey:key];
        if (nul == obj) {
            [m setObject:empty forKey:key]; 
        }
    }

    NSDictionary * result = [m copy];
    [m release];
    return result;
}

The result is the same as, and it appears pretty much identical to Jacob's, but the speed and memory requirements are one half to one third (ARC or MRC) in the tests I made. Of course, you could also use it as a category method as well.

Staceystaci answered 10/11, 2011 at 5:6 Comment(1)
Simple code, but it just replaces nulls in the root branch of an NSDictionary, so failed to remove the NULLs from my NSDictionary value, from a JSON service. Stakenborg's code below worked a treat though.Backbend
G
3

Here is my solution:

+ (NSDictionary *)cleanNullInJsonDic:(NSDictionary *)dic
{
    if (!dic || (id)dic == [NSNull null])
    {
        return dic;
    }
    NSMutableDictionary *mulDic = [[NSMutableDictionary alloc] init];
    for (NSString *key in [dic allKeys])
    {
        NSObject *obj = dic[key];
        if (!obj || obj == [NSNull null])
        {
//            [mulDic setObject:[@"" JSONValue] forKey:key];
        }else if ([obj isKindOfClass:[NSDictionary class]])
        {
            [mulDic setObject:[self cleanNullInJsonDic:(NSDictionary *)obj] forKey:key];
        }else if ([obj isKindOfClass:[NSArray class]])
        {
            NSArray *array = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
            [mulDic setObject:array forKey:key];
        }else
        {
            [mulDic setObject:obj forKey:key];
        }
    }
    return mulDic;
}


+ (NSArray *)cleanNullInJsonArray:(NSArray *)array
{
    if (!array || (id)array == [NSNull null])
    {
        return array;
    }
    NSMutableArray *mulArray = [[NSMutableArray alloc] init];
    for (NSObject *obj in array)
    {
        if (!obj || obj == [NSNull null])
        {
//            [mulArray addObject:[@"" JSONValue]];
        }else if ([obj isKindOfClass:[NSDictionary class]])
        {
            NSDictionary *dic = [self cleanNullInJsonDic:(NSDictionary *)obj];
            [mulArray addObject:dic];
        }else if ([obj isKindOfClass:[NSArray class]])
        {
            NSArray *a = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
            [mulArray addObject:a];
        }else
        {
            [mulArray addObject:obj];
        }
    }
    return mulArray;
}    
Gherkin answered 29/5, 2014 at 3:2 Comment(1)
This works like a charm in my case where the dictionary contains dictionary and array objects. Thank you :)Gates
G
2
-(NSDictionary*)stripNulls:(NSDictionary*)dict{

    NSMutableDictionary *returnDict = [NSMutableDictionary new];
    NSArray *allKeys = [dict allKeys];
    NSArray *allValues = [dict allValues];

    for (int i=0; i<[allValues count]; i++) {
        if([allValues objectAtIndex:i] == (NSString*)[NSNull null]){
            [returnDict setValue:@"" forKey:[allKeys objectAtIndex:i]];
        }
    else
        [returnDict setValue:[allValues objectAtIndex:i] forKey:[allKeys objectAtIndex:i]];
    }

return returnDict;
}
Geriatrician answered 14/7, 2013 at 21:35 Comment(0)
D
0

A category on nsnull that returns nil seems to also sense, at least to me. There are a few out there. One makes all calls return nil which seems to make sense. Sorry no link. I guess if you need to later use nspropertylistserialization the category might not work for you.

Dreda answered 28/9, 2013 at 12:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.