2012-03-28 42 views
14

下面的代碼:NSJSONSerialization沒有創造可變容器

NSError *parseError; 
NSMutableArray *listOfObjects = [NSJSONSerialization JSONObjectWithData:[@"[]" dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&parseError]; 
NSLog(@"Is mutable? %li", [listOfObjects isKindOfClass:[NSMutableArray class]]); 

listOfObjects = [NSJSONSerialization JSONObjectWithData:[@"[[],{}]" dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&parseError]; 
NSLog(@"Is mutable? %li", [listOfObjects isKindOfClass:[NSMutableArray class]]); 

正如你所看到的,我打電話正好爲這兩個時間,一個解析JSON與JSON的空單相同的方法,然後一個裏面有一個對象的列表。結果如下:

Is mutable? 0 
Is mutable? 1 

問題是NSJSONSerialization似乎沒有按照選項爲空列表創建可變容器。看起來像是一個bug,但也許我只是誤解了事情。

任何想法?

+0

你確定他們沒有出現作爲NSMutableDictionary? – Zalykr 2012-03-28 17:44:55

+0

在調試控制檯中運行'po [[listOfObjects class] superclass]'時,輸出是什麼? – warrenm 2012-03-28 17:45:48

+1

我可以在Mac OS X 10.7上確認此問題 - 只有空數組似乎受到影響。它似乎在10.8中被固定。 – blutfink 2013-01-10 22:01:36

回答

13

這個工程就像預期:

NSString *s = @"{ \"objs\": [ \"a\", \"b\" ] }";  
NSData *d = [NSData dataWithBytes:[s UTF8String] length:[s length]]; 
id dict = [NSJSONSerialization JSONObjectWithData:d options:NSJSONReadingMutableContainers error:NULL]; 

NSLog(@"%@", dict); 

[[dict objectForKey:@"objs"] addObject:@"c"]; 

NSLog(@"%@", dict); 
NSLog(@"%@", [[dict objectForKey:@"objs"] class]); 

這裏是控制檯輸出:

2012-03-28 13:49:46.224 ExampleRunner[42526:707] { 
    objs =  (
     a, 
     b 
    ); 
} 
2012-03-28 13:49:46.225 ExampleRunner[42526:707] { 
    objs =  (
     a, 
     b, 
     c 
    ); 
} 
2012-03-28 13:49:46.225 ExampleRunner[42526:707] __NSArrayM 

編輯

注意,如果我們下面的行追加到上面的代碼.. 。

NSLog(@"%@", [[dict objectForKey:@"objs"] superclass]); 

...我們得到的控制檯上的輸出如下:

2012-03-28 18:09:53.770 ExampleRunner[42830:707] NSMutableArray 

...以防萬一,目前還不清楚的是__NSArrayMNSMutableArray私有子類,由此證明了OP的代碼確實按預期工作(他的NSLog聲明除外)。

編輯

哦,順便說一下,下面的代碼行...

NSLog(@"%d", [[dict objectForKey:@"objs"] isKindOfClass:[NSMutableArray class]]); 

...結果在下面的控制檯輸出:

2012-03-28 18:19:19.721 ExampleRunner[42886:707] 1 

編輯(迴應改變的問題)

有趣...看起來像一個錯誤。考慮下面的代碼:

NSData *dictData2 = [@"{ \"foo\": \"bar\" }" dataUsingEncoding:NSUTF8StringEncoding]; 
id dict2 = [NSJSONSerialization JSONObjectWithData:dictData2 options:NSJSONReadingMutableContainers error:NULL]; 
NSLog(@"%@", [dict2 class]); 
NSLog(@"%@", [dict2 superclass]); 
NSLog(@"%d", [dict2 isKindOfClass:[NSMutableDictionary class]]); 

// This works... 
[dict2 setObject:@"quux" forKey:@"baz"]; 
NSLog(@"%@", dict2); 

NSData *dictData = [@"{}" dataUsingEncoding:NSUTF8StringEncoding]; 
id emptyDict = [NSJSONSerialization JSONObjectWithData:dictData options:NSJSONReadingMutableContainers error:NULL]; 
NSLog(@"%@", [emptyDict class]); 
NSLog(@"%@", [emptyDict superclass]); 
NSLog(@"%d", [emptyDict isKindOfClass:[NSMutableDictionary class]]); 

//...but this fails: 
[emptyDict setObject:@"quux" forKey:@"baz"]; 
NSLog(@"%@", emptyDict); 

這裏是控制檯輸出:

2012-03-29 09:40:52.781 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] __NSCFDictionary 
2012-03-29 09:40:52.782 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.783 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.783 ExampleRunner[43816:707] { 
    baz = quux; 
    foo = bar; 
} 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] __NSCFDictionary 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] NSMutableDictionary 
2012-03-29 09:40:52.784 ExampleRunner[43816:707] 1 
2012-03-29 09:40:52.785 ExampleRunner[43816:707] NSException: -[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object 

所以空數組,並以這種方式創建的字典似乎並沒有像預期的那樣。

+0

儘管[費用類]仍然是NSArray,而不是可變數組,不是? – 2012-03-28 20:35:27

+0

沒有。首先,你不能添加一個對象到'NSArray'的實例。其次,如果你看看控制檯的輸出,數組實際上是'__NSArrayM'的一個實例,它是'NSMutableArray'的私有子類。 – jlehr 2012-03-28 22:09:28

+0

嗨感謝您的回覆迄今..我已更新問題,但我想我沒有正確解釋問題..你能再次檢查? – 2012-03-29 12:54:04

1

其他人也藉此爲一個錯誤,請參閱

  1. https://github.com/couchbaselabs/TouchDB-iOS/issues/44
  2. https://github.com/johnlabarge/jlbiosutils/blob/master/jlbiosutils/DynamicProperties.m,例如。

在後一種情況下,您還可以看到完整的解決方法(對於空字典)(請參閱DynamicGetter(...)方法)。

+0

任何Jens Alfke認爲的錯誤對我來說都是一個錯誤。感謝您的鏈接,我最終以相同的方式解決了這個問題。 – 2012-07-02 23:46:54

7

下面是我對這個問題的解決方法:

#import "NSJSONSerialization+MutableBugFix.h" 

@implementation NSJSONSerialization (NSJSONSerialization_MutableBugFix) 

+ (id)JSONObjectWithDataFixed:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error { 
    id object = [NSJSONSerialization JSONObjectWithData:data options:opt error:error]; 

    if (opt & NSJSONReadingMutableContainers) { 
     return [self JSONMutableFixObject:object]; 
    } 

    return object; 
} 

+ (id)JSONMutableFixObject:(id)object { 
    if ([object isKindOfClass:[NSDictionary class]]) { 
     // NSJSONSerialization creates an immutable container if it's empty (boo!!) 
     if ([object count] == 0) { 
      object = [object mutableCopy]; 
     } 

     for (NSString *key in [object allKeys]) { 
      [object setObject:[self JSONMutableFixObject:[object objectForKey:key]] forKey:key]; 
     } 
    } else if ([object isKindOfClass:[NSArray class]]) { 
     // NSJSONSerialization creates an immutable container if it's empty (boo!!) 
     if (![object count] == 0) { 
      object = [object mutableCopy]; 
     } 

     for (NSUInteger i = 0; i < [object count]; ++i) { 
      [object replaceObjectAtIndex:i withObject:[self JSONMutableFixObject:[object objectAtIndex:i]]]; 
     } 
    } 

    return object; 
} 

@end 

所以我呼籲:

NSDictionary *object = [NSJSONSerialization JSONObjectWithDataFixed:jsonData options:NSJSONReadingMutableContainers error:&err]; 

而不是通常的:

NSDictionary *object = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; 
+0

太棒了!應該是被接受的答案... – 2015-01-29 01:30:59

1

這裏是我做的:

BOOL needsWorkaround = YES; 
if (needsWorkaround) 
{ 
    NSMutableDictionary* appState2 = 
     (__bridge_transfer NSMutableDictionary*) 
     CFPropertyListCreateDeepCopy (
      kCFAllocatorDefault, (__bridge void*)appState, 
      kCFPropertyListMutableContainersAndLeaves 
     ); 
    appState = appState2; 
}