Old McStopher Old McStopher - 23 days ago 8
iOS Question

Recursively traverse NSDictionary of unknown structure

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

Answer

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