2012-03-28 19 views
14

は、ここでは、コードだ作成していない:あなたが見ることができるように、私は、まったく同じJSONで空のリストをJSONに両方の時間、1を解析するための方法、およびを呼んでいる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]]); 

オブジェクトが内部にあるリストここでの結果です:

Is mutable? 0 
Is mutable? 1 

問題はNSJSONSerializationが空リストの変更可能なコンテナを作成するためのオプションを追跡していないようだということです。私にはバグのようですが、多分私は物事を誤解しています。

アイデア?

+0

あなたはNSMutableDictionaryとして出てこないと確信していますか? – Zalykr

+0

デバッグコンソールで 'po [[listOfObjects class] superclass]'を実行したときの出力は? – warrenm

+1

Mac OS X 10.7でこの問題を確認できます。空の配列だけが影響を受けているようです。 10.8で修正されているようです。 – blutfink

答えて

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 

EDIT我々は上記のコードに次の行を追加した場合、その

注.. 。

NSLog(@"%@", [[dict objectForKey:@"objs"] superclass]); 
これOPのコードが実際にやったことを証明し、 __NSArrayMNSMutableArrayのプライベートサブクラスであることは明らかでなかった場合に備え...

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

...私たちは、コンソールに次の出力を得ます期待どおりに動作します(彼のNSLogステートメントを除く)。

EDIT

ああ、および方法によって、次のコード行...

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

...次のコンソール出力の結果:

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

EDIT(変更された質問に応答)

興味深い...バグのようです。次のコードを考える:

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ですが、可変配列ではなく、いいですか? –

+0

まず、 'NSArray'のインスタンスにオブジェクトを追加することはできません。次に、コンソール出力を見ると、配列は実際には 'NSMutableArray'のプライベートサブクラスである' __NSArrayM'のインスタンスです。 – jlehr

+0

こんにちはお返事ありがとうございました。私は質問を更新しましたが、私は問題を正しく説明していないと考えました。もう一度確認できますか? –

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がバグと考えるものは、私にとってはバグです。リンクをありがとう、私はほぼ同じようにこれを修正して終了しました。 –

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

素晴らしい!受け入れられた答えでなければなりません... –

1

は、私が何をすべきかです:

BOOL needsWorkaround = YES; 
if (needsWorkaround) 
{ 
    NSMutableDictionary* appState2 = 
     (__bridge_transfer NSMutableDictionary*) 
     CFPropertyListCreateDeepCopy (
      kCFAllocatorDefault, (__bridge void*)appState, 
      kCFPropertyListMutableContainersAndLeaves 
     ); 
    appState = appState2; 
} 
関連する問題