В этой статье рассмотрим парсинг XML. Процедура парсинга очень простая и не представляет никакой трудности. Особенность XML-парсера в Objective-C — то, что парсер только уведомляет о нахождении каких-либо элементов и ничего не знает о «прошлом» и «будущем» документа. Парсер работает последовательно и асинхронно.

Для парсинга XML в Objective-C существует класс NSXMLParser. О нахождении каких-либо элементов парсер уведомляет свой делегат. Делегат должен следовать протоколу NSXMLParserDelegate.

Сейчас мы рассмотрим пример парсинга RSS-ленты новостей сайта Apple. Для упрощения — будем искать только заголовки статей.

Методы, которые должен поддерживать делегат, следуя протоколу NSXMLParserDelegate (и которые нас интересуют):


- (void) parserDidStartDocument:(NSXMLParser *)parser;
- (void) parserDidEndDocument:(NSXMLParser *)parser;
- (void) parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;
- (void) parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validationError;
- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict;
- (void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName;
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString )string;

Описание этих методов будет далее.

Мы напишем свой класс-делегат для NSXMLParser. Делегат будет «собирать» только заголовки статей. Если возникнет ошибка, наш делегат будет знать о ней. Делегат так же будет уведомлен об окончании парсинга и в процессе работы будет формировать результат. Назовем класс ParserDelegate.

ParserDelegate.h

@interface ParserDelegate : NSObject<nsxmlparserDelegate> {

BOOL m_done;
BOOL m_isTitle;
NSError* m_error;
NSMutableArray* m_titles;
NSMutableString* m_title;
}
// свойство-флаг, который показывает закончен ли парсинг
@property (readonly) BOOL done;
// если есть ошибка - ее описание, если нет - nil
@property (readonly) NSError* error;
// результат парсинга
@property (readonly) NSArray* titles;
@end

ParserDelegate.m

#import "ParserDelegate.h"

@implementation ParserDelegate
@synthesize done=m_done;
@synthesize titles=m_titles;
@synthesize error=m_error;
// чистка ресурсов
-(void) dealloc {
[m_error release];
[m_titles release];
[super dealloc];
}
// документ начал парситься
- (void)parserDidStartDocument:(NSXMLParser *)parser {
m_done = NO;
m_titles = [NSMutableArray new];
}
// парсинг окончен
- (void)parserDidEndDocument:(NSXMLParser *)parser {
m_done = YES;
}
// если произошла ошибка парсинга
-(void) parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
m_done = YES;
m_error = [parseError retain];
}
// если произошла ошибка валидации
-(void) parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validationError {
m_done = YES;
m_error = [validationError retain];
}
// встретили новый элемент
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
// проверяем, нашли ли мы элемент "title"
m_isTitle = [[elementName lowercaseString] isEqualToString:@"title"];
if ( m_isTitle ) {
// если да - создаем строку в которую запишем его значение
m_title = [NSMutableString new];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// если элемент title закончился - добавим строку в результат
if ( m_isTitle ) {
[m_titles addObject:m_title];
[m_title release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// если сейчас получаем значение элемента title
// добавим часть его значения к строке
if ( m_isTitle ) {
[m_title appendString:string];
}
}
@end

Немного подробнее остановимся на методе:

- (void)parser:(NSXMLParser *)parser

foundCharacters:(NSString *)string {
// если сейчас получаем значение элемента title
// добавим часть его значения к строке
if ( m_isTitle ) {
[m_title appendString:string];
}
}

В этот метод в аргументе string приходит содержимое текущего элемента. Казалось бы, можно просто сделать так:

m_title = [string copy];

Почему мы так не делаем? Все дело в том, что для некоторых элементов этот метод может вызываться несколько раз, если в содержимом встречаются некоторые особые символы.

Когда делегат для парсера создан продемонстрируем работу самого парсера. Создайте консольное Foundation-приложение:

Весь код будем распологать в файле main.m. Добавьте в проект файлы ParserDelegate.h и ParserDelegate.m, а так же в файл main.m:

#import "ParserDelegate.h"

Нам нужно: создать парсер, назначить ему делегат, дождаться завершения и вывести результат. Т.к. у нас консольное приложение — ожидание окончания парсинга будем делать «вручную».

Приведу функцию main:

int main (int argc, const char * argv[])

{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// создаем делегат
ParserDelegate* delegate = [ParserDelegate new];
// адрес RSS-ленты
NSURL* rssURL =
[NSURL
URLWithString:@"http://images.apple.com/main/rss/hotnews/hotnews.rss"];
// создаем парсер при помощи URL, назначаем делегат и запускаем
NSXMLParser* parser
= [[NSXMLParser alloc] initWithContentsOfURL:rssURL];
[parser setDelegate:delegate];
[parser parse];
// ждем, пока идет загрука и парсинг
while ( ! delegate.done )
sleep(1);
// когда парсинг окончен
// проверяем была ли ошибка парсинга
if ( delegate.error == nil ) {
// если нет то выводим результат
NSLog(@"%@",delegate.titles);
} else {
// если была - выводим ошибку
NSLog(@"Error: %@", delegate.error);
}
// освобождаем ресуры
[delegate release];
[parser release];
[pool drain];
return 0;
}

Запустите проект и в консоли вы увидите результат. У меня получилось так:

Загрузить работающий проект можно отсюда

Похожие статьи

  • [Swift] Урок 2 — Делаем простой конвертер для iOS

  • [Swift] Урок 1 — Пишем программу «Hello, World» на Swift языке под iOS

  • «Умножение» — учимся играя! (+Промокоды)

  • Ruby в iOS — встреча сообщества #iosby

  • Audio Unit в iOS. Часть 3, накладываем эффект Delay

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *