Рекурсивный обход NSDictionary неизвестной структуры - PullRequest
13 голосов
/ 08 февраля 2012

Кто-нибудь делал рекурсивный упорядоченный обход NSDictionary неизвестной структуры? Я хотел бы взять любой NSDictionary и обработать каждый уровень в иерархическом порядке.

1) Эти данные получены из проверенного JSON. Можно ли с уверенностью сказать, что NSDictionary, созданный из фреймворка, такого как SBJSON (JSON Framework), приведет только к комбинации вложенных словарей, массивов и произвольных листов?

2) Как можно выполнить общий обход с использованием быстрого перечисления, которое работает как для массивов, так и для словарей? С кодом ниже, как только я попадаю в словарь в массиве, он перестает проходить. Однако, если я продолжу рекурсию в условии массива (чтобы проверить словари в массивах), он будет прерван на следующей итерации id value = [dict valueForKey:key]; с -[__NSCFDictionary length]: unrecognized selector sent to instance SIGABRT. Я не знаю, почему это будет проблемой, потому что я уже прошел эту строку со словарем верхнего уровня (где был найден массив словарей подуровня).

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

Большое спасибо

Ответы [ 2 ]

31 голосов
/ 08 февраля 2012

Я сделал что-то похожее, где бы я проходил по структурированному объекту JSON, исходящему из веб-сервиса, и преобразовывал каждый элемент в изменчивую версию.

- (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);
    }
}
4 голосов
/ 04 апреля 2013

Мне нужно было знать имена ключей для значений, поэтому я переписал то, что имел Джоэл, и придумал это:

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

И тогда вы бы назвали это так:

[self enumerateJSONToFindKeys:JSON forKeyNamed:nil];

В качестве альтернативы, если вы хотите сделать данный путь ключа изменяемым (чтобы вы могли выполнить обратную запись, используя setValue:forKeyPath:), вы можете сделать что-то вроде следующего:

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

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...