2013-05-01 95 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