2013-05-01 30 views
4

我经常发现需要将由NSJSONSerialization创建的数据结构缓存到磁盘,并且如果-writeToFile失败(如果有空值),我需要一个在未知结构时工作的修复程序。 这是有效的,并且允许直接变异,因为NSMutableDictionary本身的实例本身没有被枚举,但它感觉有点不好意思。以递归方式从JSON结构中删除空值

这是完全好还是绝对有必要重新创建一棵树并返回?

- (void) removeNullsFromJSONTree:(id) branch 
{ 
    if ([branch isKindOfClass:[NSMutableArray class]]) 
    { 
     //Keep drilling to find the leaf dictionaries 
     for (id childBranch in branch) 
     { 
      [self removeNullsFromJSONTree:childBranch]; 
     } 
    } 
    else if ([branch isKindOfClass:[NSMutableDictionary class]]) 
    { 
     const id nul = [NSNull null]; 
     const NSString *empty = @""; 
     for(NSString *key in [branch allKeys]) 
     { 
      const id object = [branch objectForKey:key]; 
      if(object == nul) 
      { 
       [branch setObject:empty forKey:key]; 
      } 
     } 
    } 
} 
+0

如果([对象​​isKindOfClass:[NSNull空])尝试这样 – 2013-05-01 04:51:34

+0

是origionally它与'isKindOfClass'测试,但在类似的规定太问题有关消除空值(我似乎无法找到),比较指针常量更有效。 – TijuanaKez 2013-05-01 04:55:17

+0

我做了一个类别。你可以在这里找到它https://github.com/bismasaeed00/NullReplacer – bisma 2017-04-21 07:11:36

回答

13

您的一般方法没有任何问题。由于NSNull是单身人士,因此可以通过指针比较来寻找它。

但是,您不会递归于字典中的值。一般来说,这些值可能是数组或字典本身。也许在你的具体情况下,你知道他们不是。但是如果可能的话,您需要对字典中的每个值执行removeNullsFromJSONTree:

你也不会在数组中寻找NSNull。你应该?这是微不足道的处理:

[branch removeObject:[NSNull null]]; 

removeObject:方法删除参数的所有实例。

就我个人而言,我不喜欢测试对象类,因为我可以使用类别让消息发送系统为我做。所以不是我可能会NSObject定义类别如下:

// NSObject+KezRemoveNulls.h 

@interface NSObject (KezRemoveNulls) 

- (void)Kez_removeNulls; 

@end 

我会写NSObject默认无所事事的实施,并覆盖它NSMutableArrayNSMutableDictionary

// 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 

注意,所有的实现代码仍然在一个文件中。

现在我可以这样说:

id rootObject = [NSJSONSerialization JSONObjectWithData:...]; 
[rootObject Kez_removeNulls]; 
+0

我只是想知道[避免类别方法名称冲突](http://developer.apple。com/library/ios /#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#// apple_ref/doc/uid/TP40011210-CH6-SW4)适用于:*“如果某个类别中声明的方法的名称与原始类中的方法相同,或者在同一类(**或甚至超类**)上的另一个类中的方法相同,则行为未定义...“*。 – 2013-05-01 05:36:48

+0

我相信这种情况很好,因为我为超类和子类定义了相同的类别('KezRemoveNulls')。您的报价显示为“或另一个**类别中的方法”。 – 2013-05-01 05:40:45

+0

你说得对,我忽略了那部分,谢谢你的反馈! - (现在我想到了:我对NSManagedObject上的类别做了同样的事情:-) – 2013-05-01 05:46:18

9

下面是我用清理我的JSON调用的代码,似乎运作良好,但是,因为有一些涉及处理开销,我真的只在的情况下使用它我无法在服务器上执行空处理。 NSNull崩溃是我们最大的应用程序崩溃问题。

+ (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; 
    } 
} 

您可以通过传递通过NSURLConnection检索到的NSData来调用它。

NSArray *uableData = [utility cleanJsonToObject:data]; 

NSDictionary *uableData = [utility cleanJsonToObject:data]; 
+0

真是太棒了.. – svmrajesh 2014-07-19 11:05:46

+1

太棒了!我正在寻找类似的东西。你节省了写我自己方法的时间。谢谢@ Travis :) – 2015-08-25 07:19:12

-1
+ (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

@Huperniketes - > 2)将数组和字典元素设置为Nil默认为引发异常。 - >如果你运行上面的代码,这永远不会发生。 – 2016-11-28 13:02:09

+0

@Huperniketes - > 3)索引数组非常低效。改用快速枚举。 - >如果使用快速枚举,则不能将对象重新分配给实际数组。 – 2016-11-28 13:05:52

+0

1)尽管你已经很好地理解了递归,但ObjC是一种面向对象的语言,你无法使用多态来从代码中隐藏其他类的细节。将-isKindOfClass的使用替换为其各自类中的适当方法。 - >使用多态性并不总是必要的。 – 2016-11-28 13:08:14