Recursively traverse NSDictionary of unknown structure

匿名 (未验证) 提交于 2019-12-03 01:55:01

问题:

Has anyone done a recursive ordered traversal of an NSDictionary of unknown structure? I'd like to take any NSDictionary and process each level in hierarchical order.

1) This data is coming from validated JSON. Is it safe to say that the NSDictionary created from a framework such as SBJSON (JSON Framework) would only result in a combination of nested dictionaries, arrays, and arbitrary leafs?

2) How can a generic traversal be done using fast enumeration that works for both arrays and dictionaries? With the code below, once I get to a dictionary within an array, it stops traversing. However, if I continue the recursion in the array condition (to check for dictionaries within arrays), it barfs on the next iteration of id value = [dict valueForKey:key]; with a -[__NSCFDictionary length]: unrecognized selector sent to instance SIGABRT. I don't know why this would be a problem, because I already got past that line with a top-level dictionary (where the array of sub-level dictionaries were found).

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

Many thanks

回答1:

I've done something similar where I would traverse a JSON structured object coming from a web service and convert each element into a mutable version.

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


回答2:

I needed to know the key names for the values so I rewrote what Joel had and came up with this:

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

And then you'd call it like:

[self enumerateJSONToFindKeys:JSON forKeyNamed:nil];

Alternatively, if you wanted to make a given key path mutable (so you could write back to it using setValue:forKeyPath:), you could do something like below:

- (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];         }     }  }


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!