22

We're using AFNetworking in our mobile app and a lot of times we will have JSON come back that has null for some values.

I'm getting tired of doing the following.

if ([json objectForKey:@"nickname"] isKindOfClass:[NSNull class]]) {
    nickname = nil;
} else {
    nickname = [json objectForKey:@"nickname"];
}

Anything we can do to make AFNetworking automagically set objects to nil or numbers to 0 if the value is null in the JSON response?

mattt
  • 19,544
  • 7
  • 73
  • 84
birarduh
  • 762
  • 8
  • 19

6 Answers6

31

You can set flag setRemovesKeysWithNullValues to YES in AFHTTPSessionManager response serializer:

AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithBaseURL:url sessionConfiguration:config];
AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
[serializer setRemovesKeysWithNullValues:YES];
[manager setResponseSerializer:serializer];
DV_
  • 346
  • 3
  • 5
21

It's not really possible, since the dictionary can't contain nil as the object for a key. The key would have to be left out entirely in order to get the behavior you'd want, which would be undesirable in its own way.

Suppose you didn't have control over the data you were receiving and didn't know what keys were present in the JSON. If you wanted to list them all, or display them in a table, and the keys for null objects were left out of the dictionary, you'd be seeing an incorrect list.

NSNull is the "nothing" placeholder for Cocoa collections, and that's why it's used in this case.

You could make your typing a bit easier with a macro:

#define nilOrJSONObjectForKey(JSON_, KEY_) [[JSON_ objectForKey:KEY_] isKindOfClass:[NSNull class]] ? nil : [JSON_ objectForKey:KEY_]

nickname = nilOrJSONObjectForKey(json, @"nickname");
jscs
  • 63,694
  • 13
  • 151
  • 195
  • I think this is probably what I'll go with, the macro I mean. Thanks! – birarduh May 22 '12 at 01:10
  • Actually I'm not sure why leaving out the key entirely is a bad thing? I'd rather do `nickname = [json objectForKey:@"nickname"]` and have that be null and be able to just say `if(!nickname)` elsewhere. – birarduh May 22 '12 at 01:57
  • @birarda: What if you want to ask the dictionary for a list of all its keys? You would get an incorrect answer -- the key exists in the JSON. – jscs May 22 '12 at 01:58
  • I'm not sure I've ever had to do that or can think of a reason that I'd need to do that. – birarduh May 22 '12 at 02:00
  • Suppose you were displaying the whole dictionary in a table, or simply didn't know what keys were available. – jscs May 22 '12 at 02:01
  • For our purposes this is with regards to API response from our backend so we know what we expect in the dictionary. – birarduh May 22 '12 at 02:02
  • Sure, but in many other cases, one might be getting data from a service one didn't control. – jscs May 22 '12 at 02:04
  • Agreed. I think the macro is good in those instances ... I'll find a library like TouchJSON that I can use with AFNetworking that will let me set anything that comes back NULL to nil for these API calls though. Thanks! – birarduh May 22 '12 at 02:06
  • Glad I could help. I'm going to incorporate these comments into my answer so that we can clean them up later. – jscs May 22 '12 at 02:07
  • I had to modify the macro to this in order for it to work properly: #define nilOrJSONObjectForKey(JSON_, KEY_) [[JSON_ objectForKey:KEY_] isEqual:[NSNull null]] ? nil : [JSON_ objectForKey:KEY_]; – JBlake Jun 14 '13 at 05:05
2

DV_'s answer works great for AFHTTPSessionManager. But if you are using AFHTTPRequestOperation instead of the manager, try this:

AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
serializer.removesKeysWithNullValues = YES;
op.responseSerializer = serializer;
gavdotnet
  • 2,214
  • 1
  • 20
  • 30
2

There is one beautiful cocoapod called Minced https://github.com/hyperoslo/Minced that can do something that can help you handle NULL from JSON response. Instead of NULL it puts empty string.

Boris Nikolic
  • 746
  • 14
  • 24
1

If you replace the default NSJSONSerialization with SBJSON it will solve your problem.

SBJSON makes objects nil instead of NSJSONSerialization's choice of "null"

look at the requirements for the different JSON parsers you can use. https://github.com/AFNetworking/AFNetworking#requirements

Farhan Patel
  • 506
  • 3
  • 7
  • I just tried this and that doesn't seem to be the default behavior of SBJSON. If I remove the control statement to check if is null then I get this error `[NSNull stringByDecodingHTMLEntities]` when trying to set the property. – birarduh May 22 '12 at 00:59
0

You can custom AFNetworking at this functions. set any value default to objects that is NULL

static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
    NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
    for (id value in (NSArray *)JSONObject) {
        [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
    }

    return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
    NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
    for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
        id value = (NSDictionary *)JSONObject[key];
        if (!value || [value isEqual:[NSNull null]]) {
            // custom code here
            //[mutableDictionary removeObjectForKey:key];
            [mutableDictionary setObject:@"" forKey:key];
        } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
            mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
        }
    }

    return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}

return JSONObject;

}

Linh Nguyen
  • 2,006
  • 1
  • 20
  • 16