Removing nulls from a JSON structure recursively

后端 未结 4 1970
忘了有多久
忘了有多久 2021-01-05 22:46

I\'m frequently finding the need to cache data structures created by NSJSONSerialization to disk and as -writeToFile fails if there are nulls, I ne

相关标签:
4条回答
  • There's nothing wrong with your general approach. Since NSNull is a singleton, it's fine to look for it by pointer comparison.

    However, you're not recursing on the values in your dictionary. In general, those values might be arrays or dictionaries themselves. Perhaps in your specific case you know they're not. But if they could be, you need to perform removeNullsFromJSONTree: on each value in the dictionary.

    You also don't look for NSNull in an array. Should you? It's trivial to handle:

    [branch removeObject:[NSNull null]];
    

    The removeObject: method removes all instances of the argument.

    Personally I don't like testing object classes explicitly when I can use categories to let the message sending system do it for me. So instead I might define a category on NSObject like this:

    // NSObject+KezRemoveNulls.h
    
    @interface NSObject (KezRemoveNulls)
    
    - (void)Kez_removeNulls;
    
    @end
    

    I would write a default do-nothing implementation for NSObject, and override it for NSMutableArray and NSMutableDictionary:

    // NSObject+KezRemoveNulls.m
    
    #import "NSObject+KezRemoveNulls.h"
    
    @implementation NSObject (KezRemoveNulls)
    
    - (void)Kez_removeNulls {
        // nothing to do
    }
    
    @end
    
    @implementation NSMutableArray (KezRemoveNulls)
    
    - (void)Kez_removeNulls {
        [self removeObject:[NSNull null]];
        for (NSObject *child in self) {
            [child Kez_removeNulls];
        }
    }
    
    @end
    
    @implementation NSMutableDictionary (KezRemoveNulls)
    
    - (void)Kez_removeNulls {
        NSNull *null = [NSNull null];
        for (NSObject *key in self.allKeys) {
            NSObject *value = self[key];
            if (value == null) {
                [self removeObjectForKey:key];
            } else {
                [value Kez_removeNulls];
            }
        }
    }
    
    @end
    

    Note that all of the implementation code is still in one file.

    Now I could say this:

    id rootObject = [NSJSONSerialization JSONObjectWithData:...];
    [rootObject Kez_removeNulls];
    
    0 讨论(0)
  • 2021-01-05 23:36

    Swift 4 version of @Travis M.'s answer;

    class func removeNullFromJSONData(_ JSONData: Any) -> Any {
        if JSONData as? NSNull != nil {
            return JSONData
        }
    
        var JSONObject: Any!
    
        if JSONData as? NSData != nil {
            JSONObject = try! JSONSerialization.data(withJSONObject: JSONData, options: JSONSerialization.WritingOptions.prettyPrinted)
        }
        else {
            JSONObject = JSONData
        }
    
        if JSONObject as? NSArray != nil {
            let mutableArray: NSMutableArray = NSMutableArray(array: JSONObject as! [Any], copyItems: true)
            let indexesToRemove: NSMutableIndexSet = NSMutableIndexSet()
    
            for index in 0 ..< mutableArray.count {
                let indexObject: Any = mutableArray[index]
    
                if indexObject as? NSNull != nil {
                    indexesToRemove.add(index)
                }
                else {
                    mutableArray.replaceObject(at: index, with: removeNullFromJSONData(indexObject))
                }
            }
    
            mutableArray.removeObjects(at: indexesToRemove as IndexSet)
    
            return mutableArray;
        }
        else if JSONObject as? NSDictionary != nil {
            let mutableDictionary: NSMutableDictionary = NSMutableDictionary(dictionary: JSONObject as! [AnyHashable : Any], copyItems: true)
    
            for key in mutableDictionary.allKeys {
                let indexObject: Any = mutableDictionary[key] as Any
    
                if indexObject as? NSNull != nil {
                    mutableDictionary.removeObject(forKey: key)
                }
                else {
                    mutableDictionary.setObject(removeNullFromJSONData(indexObject), forKey: key as! NSCopying)
                }
            }
    
            return mutableDictionary
        }
        else {
            return JSONObject
        }
    }
    
    0 讨论(0)
  • 2021-01-05 23:44
    + (id)getObjectWithoutNullsForObject:(id)object
    {
        id objectWithoutNulls;
    
        if ([object isKindOfClass:[NSDictionary class]])
        {
            NSMutableDictionary *dictionary = ((NSDictionary *)object).mutableCopy;
    
            [dictionary removeObjectsForKeys:[dictionary allKeysForObject:[NSNull null]]];
    
            for (NSString *key in dictionary.allKeys)
            {
                dictionary[key] = [self getObjectWithoutNullsForObject:dictionary[key]];
            }
    
            objectWithoutNulls = dictionary;
        }
        else if ([object isKindOfClass:[NSArray class]])
        {
            NSMutableArray *array = ((NSArray *)object).mutableCopy;
    
            [array removeObject:[NSNull null]];
    
            for (NSUInteger index = 0; index < array.count; index++)
            {
                array[index] = [self getObjectWithoutNullsForObject:array[index]];
            }
    
            objectWithoutNulls = array;
        }
        else if ([object isKindOfClass:[NSNull class]])
        {
            objectWithoutNulls = Nil;
        }
        else
        {
            objectWithoutNulls = object;
        }
    
        return objectWithoutNulls;
    }
    
    0 讨论(0)
  • 2021-01-05 23:46

    Here's the code I'm using to clean up my JSON calls, seems to work well but, since there's some processing overhead involved I really only use it in situations where I can't do the null handling on the server. NSNull crashes is far and away our biggest app crash problem.

    + (id)cleanJsonToObject:(id)data {
        NSError* error;
        if (data == (id)[NSNull null]){
            return [[NSObject alloc] init];
        }
        id jsonObject;
        if ([data isKindOfClass:[NSData class]]){
            jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
        } else {
            jsonObject = data;
        }
        if ([jsonObject isKindOfClass:[NSArray class]]) {
            NSMutableArray *array = [jsonObject mutableCopy];
            for (int i = array.count-1; i >= 0; i--) {
                id a = array[i];
                if (a == (id)[NSNull null]){
                    [array removeObjectAtIndex:i];
                } else {
                    array[i] = [self cleanJsonToObject:a];
                }
            }
            return array;
        } else if ([jsonObject isKindOfClass:[NSDictionary class]]) {
            NSMutableDictionary *dictionary = [jsonObject mutableCopy];
            for(NSString *key in [dictionary allKeys]) {
                id d = dictionary[key];
                if (d == (id)[NSNull null]){
                    dictionary[key] = @"";
                } else {
                    dictionary[key] = [self cleanJsonToObject:d];
                }
            }
            return dictionary;
        } else {
            return jsonObject;
        }
    }
    

    You call it by passing the NSData retrieved via NSURLConnection.

    NSArray *uableData = [utility cleanJsonToObject:data];
    

    or

    NSDictionary *uableData = [utility cleanJsonToObject:data];
    
    0 讨论(0)
提交回复
热议问题