ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

Подробней о парсинге

Данные для парсинга можно получить не только с сети. XML-файл может находиться в проекте. В этом примере я продемонстрирую работу с такими файлами.

 

 

Для начала создадим новый проект. Поскольку отображать ленту новостей мы будем в таблице — создадим проект с шаблона, который уже содержит таблицу (Navigation-Based Application). Я назвал этот проект MyParser.

 

Я предлагаю работать с вот этим файлом. Его структуру задал я сам. Предполагается, что в файле хранится часть меню ресторана. Просто скачайте и добавьте этот файл в проект (в папку Supporting Files).

 

Теперь изменим интерфейс класса RootViewController:

 

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;

@interface RootViewController : UITableViewController &lt;NSXMLParserDelegate&gt; {
    NSMutableArray *dishs;
    NSString * currentElement;
    NSMutableString *description;
    NSMutableDictionary *currentDic;
}

@property (nonatomic, retain) NSMutableArray *dishs;
@property (nonatomic, retain) NSString * currentElement;
@property (nonatomic, retain) NSMutableString *description;
@property (nonatomic, retain) NSMutableDictionary *currentDic;

@end</code>

 

Как и в примере Создаем свою читалку RSS мы подключили протокол NSXMLParserDelegate и создали группу переменных. В массиве dishs у нас будут храниться данные, которые мы распарсим, с этого массива данные мы будем выводить в таблицу. currentElement нам нужен для сохранения текущего элемента, который мы парсим. description — это единственная строка, которую мы будем получать с XML-файла. А вот зачем я создал currentDic — расскажу позже.

В реализации класса RootViewController синтезируйте методы доступа и организуйте очистку памяти для этих переменных. После чего найдите, раскомментируйте и измените метод viewDidLoad:

 

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];

    self.dishs = [NSMutableArray array];

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"menu" ofType:@"xml"];  
    NSData *myData = [NSData dataWithContentsOfFile:filePath];

    NSXMLParser *rssParser = [[NSXMLParser alloc] initWithData:myData];
    rssParser.delegate = self;
    [rssParser parse];
    [rssParser release];
}</code>

 

В первую очередь мы инициализируем массив dishs. Затем с помощью метода pathForResource мы получаем путь к нашему XML-файлу и сохраняем его в переменной filePath строкового типа. Само чтение данных происходит с помощью метода dataWithContentsOfFile класса NSData, в который мы передаем значение полученного пути. В результате работы этих двух строк мы получили данные, которые, как и в предыдущем примере будем передавать парсеру. То есть, содержимое нашего XML-файла теперь храниться в переменной myData.

 

Теперь, как и в примере Создаем свою читалку RSS мы создаем парсер, передаем ему наши данные и говорим распарсить их, после чего удаляем парсер из памяти. Если все сделано правильно — сработает метод didStartElement, но если вы допустили какие-то ошибки — вызовется метод parseErrorOccurred. Реализация мтода didStartElement похожа на ту, что мы уже писали, но есть некоторые отличия. Эти отличия связаны со структурой XML-файла. Если вы обратили внимание — то название блюда хранится в атрибуте элемента, а описание блюда — в элементе description. Давайте посмотрим как выглядит реализация метода didStartElement:

 

<code data-result="[object Object]">- (void)parser:(NSXMLParser *)parser 
didStartElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qualifiedName 
    attributes:(NSDictionary *)attributeDict  
{
    self.currentElement = elementName;

    if ([elementName isEqualToString:@"dish"]) {
        self.currentDic = [NSMutableDictionary dictionaryWithCapacity:2];
        NSString *titleDish = [attributeDict objectForKey:@"title"];
        [currentDic setObject:titleDish forKey:@"title"];
    } else if ([elementName isEqualToString:@"description"]) {
        self.description = [NSMutableString string];   
    }
}</code>

 

Чтобы глубже понять смысл кода и его связь с файлом я сделал это схематически:

 

 

Все атрибуты элемента мы получаем в словаре  attributeDict метода didStartElement, а ключами этого словаря являются имена атрибутов. Сначала переменной currentElement мы присваиваем имя текущего элемента с которым работает парсер. Затем проверяем, если имя текущего элемента равно dish — считываем и сохраняем в словаре currentDic значение атрибута title. Если имя элемента description — инициализируем переменную description, в которой будем хранить значения этого элемента.

В методе foundCharacters мы проверяем работает ли парсер с элементом description, если да — считываем его значения и сохраняем в переменной description:

 

<code data-result="[object Object]">- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if ([currentElement isEqualToString:@"description"]) {
        [description appendString:string];
    }
}</code>

 

Метод didEndElement вызывается, когда все значения элемента считаны.

 

<code data-result="[object Object]">- (void)parser:(NSXMLParser *)parser 
 didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qName 
{
    if ([elementName isEqualToString:@"description"]) {
        [currentDic setObject:description forKey:@"description"];
        [dishs addObject:currentDic];
        self.currentDic = nil;
        self.description = nil;
    }
}</code>

 

В нем мы так же проверяем, если имя элемента, который мы считали равно description — сохраняем значения переменной description в словарь currentDic. После чего добавляем значения словаря в массив dishs и очищаем переменные currentDic и description для использования с новыми объектами XML-файла.

Когда парсер закончит обработку данных вызовется метод parserDidEndDocument.

 

<code data-result="[object Object]">- (void)parserDidEndDocument:(NSXMLParser *)parser {
    [self.tableView reloadData];
}</code>

 

В нем мы просто перегружаем нашу таблицу. Таким образом таблица снова обратиться за данными к нашему массиву dishs, который мы только что заполнили.

Осталось организовать правильную работу таблицы. В методе numberOfRowsInSection указываем количество элементов массива.

 

<code data-result="[object Object]">- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [dishs count];
}</code>

 

А в методе cellForRowAtIndexPath изменим тип ячейки, получим с массива блюд словарь, который мы добавляли туда в момент парсинга. По известным нам ключам получаем данные с массива и заполняем их в поля ячейки.

 

<code data-result="[object Object]">- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
                                       reuseIdentifier:CellIdentifier] autorelease];
    }

    NSDictionary *dic = [dishs objectAtIndex:indexPath.row];

    cell.textLabel.text = [dic objectForKey:@"title"];
    cell.detailTextLabel.text = [dic objectForKey:@"description"];

    return cell;
}</code>

 

Этого достаточно для полноценной работы программы. Исходный код можно скачать здесь.

Comments are closed.