Проблемы с преобразованием вложенного Plist в NSDictionaries & Arrays

Учитывая этот словарь P-List:

введите здесь описание изображения

Как попасть на 3-й. Ключ - "Ужин" - который сам по себе тоже Словарь, и правильно разбирать его значения?

Или мне следует с самого начала структурировать этот P-список по-другому, чтобы мне было легче добираться до всего?

Вот что я получил, начав с захвата всех ключей из моего «MenuDictionary» и сохранения их в массиве:

// Load the Property-List file into the Dictionary:
MenuDictionary = [[NSDictionary alloc] initWithContentsOfFile:menuPath];

// Get all the Keys from the Dictionary, put 'em into a 'mealTimesArray':
mealTimesArray = [[MenuDictionary allKeys] sortedArrayUsingSelector:@selector(compare:)];

// For each meal-type KEY, grab all the Values (dishes) in it and store them in a 'mealDishesArray':
for (NSString *mealTime in mealTimesArray) {
    NSArray *mealDishesArray = [MenuDictionary valueForKey:mealTime];

    // Now I can iterate through the 'mealDishesArray' to access each dish at
    // a time, so I can print them out or do whatever else:
    for (NSString *dish in mealDishesArray) {
        NSLog(@"Iterating through 'mealDishesArray' now...");
        NSLog(@"Current 'dish' is: %@", dish);

Проблема возникает, когда я добираюсь до ключа «Ужин»: это словарь, содержащий 2 ключа с 2 значениями массива. Итак, как мне загрузить его содержимое в объект Dictionary? В частности, какой метод «init» я должен использовать для загрузки содержимого «Ужин» в мой новый объект Dictionary?

Пробовал так - не работает:

// I put this inside the first fast-enum loop:

if ([mealTime isEqualToString: @"Dinner"]) {
   // init new Dictionary object (declared previously):
   dinnerDictionary = [[NSDictionary alloc] initWith ???];

Я хотел бы запустить его с содержимым ключа «Ужин», но, очевидно, это не файл P-List, поэтому я не могу использовать

   initWithContentsOfFile: pathName

Я не понимаю, какой из других методов инициализации даст мне доступ как к ключам, так и к значениям «Ужина». Потому что, хотя «Ужин» структурирован как словарь, в настоящее время он находится внутри массива, который не считает его словарем (я думаю...)

Я немного не понимаю этого, очевидно.

Или мне следует для начала по-другому структурировать свой P-List, чтобы я мог получить доступ к этому вложенному словарю Dinner?

Есть идеи?


person sirab333    schedule 21.04.2012    source источник


Ответы (2)


Я думаю, что структура plist имеет смысл, и работать с содержимым, основанным на условном классе, тоже совершенно нормально. Я бы отреагировал на то, что в plist, в пределах разумного диапазона ожиданий, так что...

// for each meal...
for (NSString *mealTime in mealTimesArray) {
    // we're not sure what kind of meal we have
    id mealInfo = [MenuDictionary valueForKey:mealTime];

    if ([id isKindOfClass:[NSArray self]]) {
        // it's an array? cast as an array and deal with the array
        NSArray *mealDishesArray = (NSArray *)mealInfo;
        [self handleMealArray:mealDishesArray];
    } else if ([id isKindOfClass:[NSDictionary self]]) {
        // it's a dictionary?  that's cool, too. cast as a dictionary and deal with it
        NSDictionary *mealDictionary = (NSDictionary *)mealInfo;
        [self handleMealDictionary:mealDictionary];
    }
}

// you've worked out to handle the array
- (void)handleMealArray:(NSArray *)mealDishesArray {
    for (NSString *dish in mealDishesArray) {
        NSLog(@"Iterating through 'mealDishesArray' now...");
        NSLog(@"Current 'dish' is: %@", dish);
    }
}

// handle the dictionary like a dictionary, realizing that it contains
// arrays, which you've already worked out how to handle
- (void)handleMealDictionary:(NSDictionary *)mealDictionary {
    for (NSString *dishType in [mealDictionary allKeys]) {
        NSArray *mealDishesArray = [mealDictionary valueForKey:dishType];
        [self handleMealArray:mealDishesArray];
    }
}
person danh    schedule 21.04.2012
comment
Не с чем не согласиться. Вы читали после "Каково решение"? В первой части описывалась проблема, которую можно было четко наблюдать с помощью NSAssert() (чего автор поста не ожидал). - person GoZoner; 22.04.2012
comment
@GoZoner - я прочитал, чтобы они были одного типа, и остановился. Виноват. Отредактирую свое несогласие, но оставлю уважение. - person danh; 22.04.2012
comment
@danh- очень интересный подход. Мне особенно нравится идея приведения объекта, если/когда это необходимо (по какой-то причине я никогда не думаю об использовании приведения в качестве решения проблем - нужно больше читать об этом; любопытно, есть ли какие-либо ограничения - например: можно что-либо может быть преобразовано во что-либо еще?) Также, например, как вы разбиваете его на разные методы обработки. Эффектный и элегантный. Я попробую это и дам вам знать, как это происходит. - person sirab333; 22.04.2012
comment
Конечно. Актерский состав менее силен, чем может показаться. Думайте об этом больше как о получении разрешения от компилятора обрабатывать определенный объект определенным образом. Но объект во время выполнения не изменится. Если вы разыгрываете Foo в Bar и используете его как Bar, даже если это действительно Foo, вы падаете. Удачи. - person danh; 22.04.2012
comment
хорошо, это сработало :-) Мне пришлось добавить строку кода, чтобы также сохранить фактические названия Dinner KEYS (Meat, Vegetarian), но в остальном это было прекрасно - и это имеет смысл. Теперь мне нужно выяснить, как заставить этот PList заполнять контроллер UITableView, чтобы создавать раскрывающиеся меню :-) (Не говоря уже о том, чтобы иметь возможность обновлять этот файл PList - возможно, из веб-формы в базу данных MySQL, через PHP, анализируется как JSON и т. д. и т. д. :-) Впереди хорошие времена :-) В любом случае, пока еще раз спасибо. - person sirab333; 23.04.2012

«Проблема» заключается в этой строке:

NSArray *mealDishesArray = [MenuDictionary valueForKey:mealTime];

когда foodTime имеет значение «Ужин», вы присваиваете едойDishesArray значение, которое является NSDictionary. Думая, что у вас есть массив, который вы затем используете:

for (NSString *dish in mealDishesArray)

для перебора элементов в массиве, который не даст вам того, что вы ожидаете от «Ужина». Вы можете добавить что-то вроде:

NSAssert ([mailDishesArray isKindOfClass: [NSArray class]], @"Expecting an array");

после вашего назначения для едыDishesArray.

Каково решение? В вашем PLIST совершенно другая структура: «Завтрак», «Обед» и «Ужин». Почему «Ужин» — это NSDictionary, а остальные — NSArray? Сделайте их все одного типа. Если это невозможно, вы должны обусловить свой код на основе:

if ([mealTime isEqualToString: @"Dinner"]) {
   NSDictionary *dinnerDictionary = (NSDictionary *) [MenuDictionary valueForKey:mealTime];
   /* ... */ }

Вам не нужно ничего выделять или читать что-либо из файла; у вас уже есть словарь для данных «Ужин».

person GoZoner    schedule 21.04.2012
comment
Я думаю, что ваше предложение по кастингу очень хорошее, и я попробую его. Однако с точки зрения структуры PLIST вы сказали: сделать их все однотипными — как я могу? Мы уже знаем, что ужин не может быть обычным массивом, потому что его содержимое должно быть структурировано как словарь (если вы не предлагаете многомерный массив?). Между тем, завтрак и обед явно не словари, так как/почему я должен их сделать быть? Просто чтобы соответствовать структуре ужина? Я не понимаю. - person sirab333; 22.04.2012
comment
Они не должны быть одинаковыми. Я же написал, если их не может быть, то вы... - так что условьтесь на "Ужин". - person GoZoner; 22.04.2012