2012-02-08 9 views
13

Qualcuno ha fatto una traversata ordinata ricorsiva di un NSDictionary di struttura sconosciuta? Mi piacerebbe prendere qualsiasi NSDictionary e processare ogni livello in ordine gerarchico.Attraversare in modo ricorsivo NSDictionary di struttura sconosciuta

1) Questi dati provengono da JSON convalidato. È sicuro dire che il NSDictionary creato da un framework come SBJSON (JSON Framework) risulterebbe in una combinazione di dizionari nidificati, array e foglie arbitrarie?

2) Come si può effettuare un attraversamento generico utilizzando l'enumerazione rapida che funziona sia per gli array che per i dizionari? Con il codice qui sotto, una volta arrivato a un dizionario all'interno di un array, smette di attraversare. Tuttavia, se continuo la ricorsione nella condizione dell'array (per verificare la presenza di dizionari all'interno degli array), si blocca sulla successiva iterazione di id value = [dict valueForKey:key]; con un SIGABRT -[__NSCFDictionary length]: unrecognized selector sent to instance. Non so perché questo sarebbe un problema, perché ho già superato quella linea con un dizionario di primo livello (dove è stata trovata la serie di dizionari di sottolivello).

-(void)processParsedObject:(id)dict counter:(int)i parent:(NSString *)parent 
{ 
    for (id key in dict) { 
     id value = [dict valueForKey:key]; 
     NSLog(@"%i : %@ : %@ -> %@", i, [value class], parent, key); 

     if ([value isKindOfClass:[NSDictionary class]]) 
     { 
      i++; 
      NSDictionary* newDict = (NSDictionary*)value; 
      [self processParsedObject:newDict counter:i parent:(NSString*)key]; 
      i--; 
     } 
     else if ([value isKindOfClass:[NSArray class]]) 
     { 
      for (id obj in value) { 
       NSLog(@"Obj Type: %@", [obj class]); 
      } 
     } 
    } 
} 

Molte grazie

+0

Si esegue questa operazione ogni volta che si esegue il log di un NSDictionary. –

+0

Giusto, ma il punto in generale è quello di acquisire ed elaborare gli oggetti nidificati nel processo, non solo registrarli. –

+0

Hai solo bisogno di imparare come usare 'isKindOfClass'. –

risposta

31

Ho fatto qualcosa di simile in cui vorrei attraversare un oggetto JSON strutturato proveniente da un servizio web e convertire ogni elemento in una versione mutevole.

- (void)processParsedObject:(id)object 
{ 
    [self processParsedObject:object depth:0 parent:nil]; 
} 

- (void)processParsedObject:(id)object depth:(int)depth parent:(id)parent 
{  
    if ([object isKindOfClass:[NSDictionary class]]) 
    { 
     for (NSString* key in [object allKeys]) 
     { 
      id child = [object objectForKey:key]; 
      [self processParsedObject:child depth:(depth + 1) parent:object]; 
     } 
    } 
    else if ([object isKindOfClass:[NSArray class]]) 
    { 
     for (id child in object) 
     { 
      [self processParsedObject:child depth:(depth + 1) parent:object]; 
     } 
    } 
    else 
    { 
     // This object is not a container you might be interested in it's value 
     NSLog(@"Node: %@ depth: %d", [object description], depth); 
    } 
} 
+0

Eccellente. Darò uno scatto e riferirò. –

+0

Funziona come un fascino! Mi piace come l'hai fattorizzato con un metodo one-param, quindi il chiamante non ha bisogno di conoscere parametri oscuri. –

+0

Perché passi "genitore"? Sembra che non sia mai stato usato. –

4

avevo bisogno di sapere i nomi delle chiavi per i valori così ho riscritto quello che doveva Joel e si avvicinò con questo:

- (void)enumerateJSONToFindKeys:(id)object forKeyNamed:(NSString *)keyName 
{ 
    if ([object isKindOfClass:[NSDictionary class]]) 
    { 
     // If it's a dictionary, enumerate it and pass in each key value to check 
     [object enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { 
      [self enumerateJSONToFindKeys:value forKeyNamed:key]; 
     }]; 
    } 
    else if ([object isKindOfClass:[NSArray class]]) 
    { 
     // If it's an array, pass in the objects of the array to check 
     [object enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 
      [self enumerateJSONToFindKeys:obj forKeyNamed:nil]; 
     }]; 
    } 
    else 
    { 
     // If we got here (i.e. it's not a dictionary or array) so its a key/value that we needed 
     NSLog(@"We found key %@ with value %@", keyName, object); 
    } 
} 

E poi chiamerei come:

[self enumerateJSONToFindKeys:JSON forKeyNamed:nil]; 

In alternativa, se si desidera rendere mutevole un determinato percorso chiave (in modo da poterlo riscrivere utilizzando setValue:forKeyPath:), è possibile eseguire alcune cosa come di seguito:

- (void)makeDictionariesMutableForKeyPath:(NSString *)keyPath { 
    NSArray *keys = [keyPath componentsSeparatedByString:@"."]; 

    NSString *currentKeyPath = nil; 
    for (NSString *key in keys) { 
     if (currentKeyPath) { 
      NSString *nextKeyPathAddition = [NSString stringWithFormat:@".%@", key]; 
      currentKeyPath = [currentKeyPath stringByAppendingString:nextKeyPathAddition]; 
     } else { 
      currentKeyPath = key; 
     } 

     id value = [self.mutableDictionary valueForKeyPath:currentKeyPath]; 
     if ([value isKindOfClass:NSDictionary.class]) { 
      NSMutableDictionary *mutableCopy = [(NSDictionary *)value mutableCopy]; 
      [self.mutableDictionary setValue:mutableCopy forKeyPath:currentKeyPath]; 
     } 
    } 

} 
+0

Ciao, c'è un modo per restituire tutto ketPath? ... qualcosa come "keyname.keyname.keyname" per tutte le sottochiavi ?? – Mike97

+0

... oops ... keypath – Mike97

Problemi correlati