2012-03-28 10 views
14

Ecco il codice:NSJSONSerialization non la creazione di contenitori mutevoli

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

Come potete vedere, sto chiamando esattamente lo stesso metodo per l'analisi del JSON entrambe le volte, una con una lista vuota in JSON, e poi una lista con un oggetto dentro Ecco il risultato:

Is mutable? 0 
Is mutable? 1 

Il problema è che il NSJSONSerialization non sembra seguire la possibilità di creare contenitori mutevoli per gli elenchi vuoti. Sembra un bug per me, ma forse ho solo frainteso le cose.

Qualche idea?

+0

Sei sicuro che non vengano fuori come NSMutableDictionary? – Zalykr

+0

Qual è l'output quando si esegue la 'po [[listOfObjects class] superclasse]' nella console di debug? – warrenm

+1

Posso confermare questo problema su Mac OS X 10.7: solo gli array vuoti sembrano essere interessati. Sembra essere risolto in 10.8. – blutfink

risposta

13

Questo funziona proprio come previsto:

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

Ecco l'output della console:

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

Nota che se ci aggiungiamo la seguente riga al codice di cui sopra ..

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

... si ottiene il seguente output sulla console:

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

... nel caso in cui non era chiaro che __NSArrayM è una sottoclasse privata di NSMutableArray, dimostrando così che il codice del PO ha effettivamente funziona come previsto (fatta eccezione per la sua dichiarazione NSLog).

EDIT

Oh, e dal modo in cui, la seguente riga di codice ...

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

... genera il seguente output della console:

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

MODIFICA (risponde alla domanda modificata)

Interessante ... sembra un insetto. Dato il seguente codice:

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

Ecco l'output della console:

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 

array e dizionari creati in questo modo così vuoto non sembrano comportarsi come previsto.

+0

La [classe di spese] è ancora un NSArray sebbene, invece di un array mutabile, no? –

+0

No. Innanzitutto, non è possibile aggiungere un oggetto a un'istanza di 'NSArray'. In secondo luogo, se si guarda l'output della console, la matrice è in realtà un'istanza di '__NSArrayM', che è una sottoclasse privata di' NSMutableArray'. – jlehr

+0

Ciao Grazie per la risposta fino ad ora .. Ho aggiornato la domanda però, ho pensato che non stavo spiegando il problema correttamente .. puoi controllare di nuovo? –

1

Altri prendono anche questo per un bug, vedere

  1. https://github.com/couchbaselabs/TouchDB-iOS/issues/44 o
  2. https://github.com/johnlabarge/jlbiosutils/blob/master/jlbiosutils/DynamicProperties.m, per esempio.

In quest'ultimo caso è possibile visualizzare una soluzione completa anche per i dizionari vuoti (vedere il metodo DynamicGetter(...)).

+0

Tutto ciò che Jens Alfke considera un bug è un bug per me. Grazie per i collegamenti, ho finito per sistemarlo più o meno allo stesso modo. –

7

Ecco la mia soluzione per questo problema:

#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 

Così mi chiamano:

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

Invece del solito:

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

fantastico! dovrebbe essere la risposta accettata ... –

1

Ecco quello che faccio:

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