Recursively traverse NSDictionary of unknown structure
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); }}
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]; } }}