Статьи

Разбор XML-файлов с помощью Objective-C

Пожалуй, самое простое приложение для создания — это программа для чтения каналов блогов. Как правило, вы не найдете собственного мобильного приложения, которое предназначено только для отображения канала. Обычно эту задачу лучше всего оставить для веб-сайта или веб-приложения.

Начиная с основ, RSS-каналы обычно представляют собой XML, который использует специальные теги, чтобы сообщить странице, какой тип информации отображается. Например, вместо отображения текста «Автор: Джо Шмоэ» код будет выглядеть более

  <author> Джо Шмоэ </ author> 

Первый формат более удобен для тех, кто просматривает XML-файл в браузере, а другой более удобен для поисковых систем и браузеров. Другое преимущество использования XML над обычным текстом заключается в том, что его легко разбить на отдельные части, а не просто смотреть на текст, чтобы разбить файл на части. Мы будем смотреть на « Books.xml ».

Как мы можем сделать это в наших приложениях? Давайте запустим XCode и приступим к работе. Во-первых, давайте создадим новый проект, я назвал мой RSSFeed. Обратите внимание, что я использую XCode 4.2 на Mac OSX Lion, который является последней версией. Так что это может выглядеть немного иначе для вас — при настройке проекта выберите «Single View Application» в качестве типа проекта и убедитесь, что автоматический подсчет ссылок (ARC) не отмечен. Это позволит нам настроить контроллер представления, делегат приложения и файл пера. Nib-файлы — это визуальная сторона приложения.

Создайте новый проект в Xcode 4

Нам нужно создать файл для хранения информации о текущем элементе, который мы просматриваем, и в этом случае это будет книга. поэтому нам нужно создать новый подкласс NSObject. Для этого перейдите в File -> New -> New File. Под iOS выберите сенсорный раздел какао, затем выберите класс Objective-C.

Добавить новый класс Objective-C

Я назвал мою Книгу. На основе XML-файла нам необходимо сохранить четыре элемента: автор, заголовок, резюме и индекс книги. Author, title и summary — все строки, а индекс книги — целое число.

Это очень простой объект, состоящий только из переменных для хранения данных, кроме функции не требуется никаких методов. Как и в любом другом приложении, нам нужно создать переменные в заголовочном файле и создать свойства для них. Затем в файле реализации нам нужно синтезировать эти переменные и освободить их в функции dealloc. Вот как они должны выглядеть.

// Book.h
#import <Foundation/Foundation.h>

@interface Book : NSObject {
    NSInteger bookID;
    NSString *author;
    NSString *title;
    NSString *summary;
}

@property (nonatomic, readwrite) NSInteger bookID;
@property (nonatomic, retain) NSString *author;
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *summary;

@end

 // Book.m
#import "Book.h"

@implementation Book

@synthesize author, title, summary, bookID;

- (void) dealloc
{
    [author release];
    [summary release];
    [title release];
    [super dealloc];
}

@end

Теперь, когда мы настроили это, давайте создадим файл парсера. Нам нужно пройти через тот же процесс, чтобы создать новый класс на основе Objective C, назовите этот rssParser. Следующий код должен быть добавлен в файл rssParser.h:

 #import <UIKit/UIKit.h>

@class Book, AppDelegate;

@interface rssParser : NSObject <NSXMLParserDelegate>{

    Book *book;
    AppDelegate *appdelegate;

    NSMutableString *curElem;

}

@property (nonatomic, retain) Book *book;
@property (nonatomic, retain) AppDelegate *appdelegate;
@property (nonatomic, retain) NSMutableString *curElem;

- (rssParser*) initXMLParser;

@end

Как видите, этот файл использует фреймворк UIKit. Прямо под этим у вас есть так называемые классы-оболочки. Когда вы используете классы-обертки, вы разрешаете вашему классу обращаться не только к методам, но и к переменным тех классов, которые в данном случае являются Book и AppDelegate. Чтобы достичь этих переменных и методов, нам нужно создать экземпляры этих классов. Это то, что Книга * книга; AppDelegate * appdelegate; выполняет. Затем мы создаем строку для хранения текущего элемента. Как и прежде, нам нужно добавить свойства и затем синтезировать переменные.

Я знаю, что сейчас это немного сухо, но это базовая настройка, чтобы все заработало. Вы также увидите объявление метода для функции initXMLParser, к которому мы перейдем далее. Теперь нам нужно соединить части в файле реализации класса rssParser. Добавьте следующие заголовки в начало файла rssParser.m, чтобы соединить файлы вместе: AppDelegate.h и Book.h. Помните ту функцию initXMLParser, которую мы объявили? Ну вот код, который составляет эту функцию.

Я уверен, что вы можете сказать, что этот метод инициализирует все части, необходимые для запуска парсера. Сначала вы инициализируете сам анализатор, а затем AppDelegate. Давайте перейдем к файлам AppDelegate.h и AppDelegate.m, чтобы вставить немного больше кода, прежде чем получить лучшее представление о том, что здесь происходит. Начиная с AppDelegate.h, добавьте следующий код:

 - (rssParser *) initXMLParser
{
    [super init];

    appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

    return self;
}

Это не должно слишком отличаться от того, с чем файл был первоначально создан. В действительности единственными строками кода, которые были добавлены в файл, является создание и свойство NSMutableArray, называемого books. Здесь вместо того, чтобы воссоздавать весь файл, должны произойти только три вещи.

  • Нам нужно добавить ссылку на файл rssParser.h, чтобы иметь к нему доступ.
  • Нам нужно синтезировать наш массив книг
  • Соедините точки и заставьте все работать.

Добавьте следующее в функцию didFinishLaunchingWithOptions приложения сразу после первой строки кода в функции.

 NSURL *url = [[NSURL alloc] initWithString:@"http://sites.google.com/site/iphonesdktutorials/xml/Books.xml"];
NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithContentsOfURL:url];

rssParser *parser = [[rssParser alloc] initXMLParser];

[xmlparser setDelegate:parser];

BOOL success = [xmlparser parse];

if(success){
	NSLog(@"No Errors");
}
else{
	NSLog(@"Error Error Error!!!");
}

Время разбить код на части. Каждый раз, когда вы работаете с файлом в Objective-C, вы должны ссылаться, используя переменную типа данных NSUrl. Поскольку мы используем файл, который не содержится в приложении, мы можем инициализировать этот URL-адрес, используя строку. Теперь нам нужно создать парсер, но обратите внимание: мы еще не используем класс rssParser. Objective C имеет встроенный класс синтаксического анализа XML под названием NSXMLParser. Мы можем использовать созданный нами URL-адрес, чтобы передать файл в анализатор для последующей обработки приложением.

Чтобы двигаться дальше, нам нужно инициализировать наш класс rssParser, используя функцию initXMLParser, которую мы создали ранее. Кроме того, помните, что в rssParser.h мы должны были объявить его как XMLParserDelegate, здесь мы применяем это к анализатору XML.

Назначение делегата — переопределить базовые функциональные возможности, чтобы дать пользовательской обработке синтаксический анализатор, но мы разберемся с этим через несколько минут. Последние несколько строк кода здесь проверяют, был ли разбор успешным, и добавляют сообщение в журнал в XCode в качестве доказательства. Обратите внимание, что мы используем переменную xmlparser для вызова функции разбора. Обычно это выполняется с помощью основных функций, но мы хотим настроить его. Итак, теперь нам нужно вернуться в rssParser.m и добавить функции для анализа информации. Этот делегат переопределит три метода, которые приходят из класса XMLParser: didStartElement, foundCharacters и didEndElement.

 -(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
    if([elementName isEqualToString:@"Books"]) {
        //Initialize the array.
        appdelegate.books = [[NSMutableArray alloc] init];
    }
    else if([elementName isEqualToString:@"Book"]) {

        //Initialize the book.
        book = [[Book alloc] init];

        //Extract the attribute here.
        book.bookID = [[attributeDict objectForKey:@"id"] integerValue];

        NSLog(@"Reading id value :%i", book.bookID);
    }

    NSLog(@"Processing Element: %@", elementName);
}

Эта функция обрабатывает, что делать, когда элемент найден. В XML-файле, на который мы ссылаемся, «Книги» — это имя оболочки фида. Таким образом, если это элемент, который был найден, то это начало файла, поэтому мы инициализируем массив книг, который мы создали в AppDelegate. «Книга» (не во множественном числе) — это объявление отдельного элемента в файле. Теперь мы должны хранить данные локально в объекте Book. Также обратите внимание, что этот процесс записывает начало процесса синтаксического анализа в файле журнала в XCode. Теперь перейдем к методу foundCharacters.

 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

    if(!curElem)
        curElem = [[NSMutableString alloc] initWithString:string];
    else
        [curElem appendString:string];

    NSLog(@"Processing Value: %@", curElem);

}

Эта функция просто используется для хранения значений элементов по мере их обработки и записи их в журнал. И для конца элемента:

 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

    if([elementName isEqualToString:@"Books"])
        return;

    if([elementName isEqualToString:@"Book"]) {
        [appdelegate.books addObject:book];

        [book release];
        book = nil;
    }
    else
        [book setValue:curElem forKey:elementName];

    [curElem release];
    curElem = nil;
}

Эта функция делает пару вещей. Сначала он снова проверяет элемент оболочки. На этот раз, если он найден, он завершает процесс анализа. Если синтаксический анализатор снова находит элемент книги, он берет всю информацию, найденную в этом элементе, добавляет ее в массив книг, а затем очищает значение, сохраненное в curElem, для использования следующим элементом в строке. Давайте повторим, что делает это приложение с точки зрения процесса приложения. Сначала, когда приложение загружается, мы объявляем, где находится файл XML, создаем экземпляр анализатора XML и помещаем файл XML в анализатор.

Мы хотели переопределить обычную функциональность синтаксического анализа, поэтому мы установили делегат для нашего делегата rssParser, который мы создали. В этом делегате мы переопределяем три функции для обработки файла XML: didStartElement, foundCharacters и didEndElement. В этих функциях мы ссылаемся на наш объект Book для временного хранения любой найденной информации, записываем процесс в выходной журнал, а когда элемент был полностью проанализирован, добавляем коллекцию данных в массив, который можно использовать позже.

Теперь запустите ваш код и откройте журнал вывода в XCode, чтобы увидеть, успешно ли работает ваш код. В конце этих журналов вы должны увидеть «Нет ошибок» жирным шрифтом, а также вы увидите все элементы, которые были обработаны из XML-файла. С этого момента есть много способов отобразить ваш XML-фид, так что пусть ваш творческий потенциал иссякнет!