2012-03-28 4 views
14

Hier ist der Code:NSJSONSerialization Erstellung nicht wandelbar Container

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]]); 

Wie Sie sehen können, ich rufe genau die gleiche Methode für die JSON beiden Male Parsen, eines mit einer leeren Liste im JSON, und dann eine Liste mit einem Objekt darin. Hier ist das Ergebnis:

Is mutable? 0 
Is mutable? 1 

Das Problem ist, dass die NSJSONSerialization nicht wandelbar Container scheint die Möglichkeit zu folgen für leere Listen zu erstellen. Scheint wie ein Fehler für mich, aber vielleicht habe ich nur Dinge falsch verstanden.

Irgendwelche Ideen?

+0

Sind Sie sicher, dass sie nicht als NSMutableDictionary ausgegeben werden? – Zalykr

+0

Was ist die Ausgabe, wenn Sie 'po [[listOfObjects Klasse] Superklasse]' in der Debug-Konsole ausführen? – warrenm

+1

Ich kann dieses Problem unter Mac OS X 10.7 bestätigen - nur leere Arrays scheinen betroffen zu sein. Es scheint in 10.8 behoben zu sein. – blutfink

Antwort

13

Dies funktioniert genauso wie erwartet:

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]); 

Hier ist die Ausgabe der Konsole:

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

Beachten Sie, dass, wenn wir die folgende Zeile in dem obigen Code anhängen ..

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

... wir erhalten die folgende Ausgabe auf der Konsole:

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

... nur für den Fall es nicht klar war, dass __NSArrayM eine private Unterklasse von NSMutableArray ist, was beweist, dass der Code des OP tat in der Tat Arbeiten wie erwartet (außer für seine NSLog Aussage).

EDIT

Oh, und übrigens, die folgende Codezeile ...

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

... Ergebnisse werden in der folgenden Ausgabe der Konsole:

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

BEARBEITEN (auf geänderte Frage antworten)

Interessant ... sieht aus wie ein Käfer. Angesichts der folgenden Code:

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); 

Hier ist die Ausgabe der Konsole:

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 

So leere Arrays und auf diese Weise erstellt Wörterbücher scheinen nicht wie erwartet zu verhalten.

+0

Die [Ausgabenklasse] ist immer noch ein NSArray, anstelle eines veränderbaren Arrays, nein? –

+0

Nein. Erstens können Sie kein Objekt zu einer Instanz von 'NSArray' hinzufügen. Zweitens, wenn Sie sich die Konsolenausgabe ansehen, ist das Array tatsächlich eine Instanz von '__NSArrayM', was eine private Unterklasse von' NSMutableArray' ist. – jlehr

+0

Hallo Danke für die Antwort so weit .. Ich habe die Frage aktualisiert, aber ich dachte, ich habe das Problem nicht richtig erklärt .. können Sie noch einmal überprüfen? –

1

Andere nehmen dies auch für einen Fehler finden

  1. https://github.com/couchbaselabs/TouchDB-iOS/issues/44 oder
  2. https://github.com/johnlabarge/jlbiosutils/blob/master/jlbiosutils/DynamicProperties.m, zum Beispiel.

Im letzteren Fall können Sie komplette Abhilfe auch für leere Wörterbücher (siehe DynamicGetter(...) Methode).

+0

Alles, was Jens Alfke für einen Bug hält, ist ein Fehler für mich. Danke für die Links, das habe ich letztendlich genauso gelöst. –

7

Hier ist meine Abhilfe für dieses Problem:

#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 

So nenne ich:

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

Statt der üblichen:

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

großartig! sollte die akzeptierte Antwort sein ... –

1

Hier ist, was ich tue:

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