Обработка элементов XML и атрибутов

Обычно при парсинге XML-документа, большая часть обработки включает элементы и вещи, связанные с элементами, такими как атрибуты и текстовое содержание. Элементы содержат большую часть информации в XML-документе. Когда объект NSXMLParser пересекает элемент в XML-документе, он отправляет по крайней мере три отдельных сообщения своему делегату в следующем порядке:

Синтаксический анализатор мог бы отправить parser:foundCharacters: обменивайтесь сообщениями многократно для одного элемента; однако, если символы состоят из только пробельных символов (пространство, новая строка, вкладка и подобные символы), синтаксический анализатор отправляет parser:foundIgnorableWhitespace: вместо этого.

При парсинге элементов XML усовершенствованный метод, который можно принять, должен переключить ответственность за обработку среди многократных делегатов, каждый из которых знает, как обработать определенный тип элемента. Для получения дополнительной информации посмотрите Используя Многократных Делегатов.

Конструктивные соображения

В объектно-ориентированной среде, такой как Какао, общая стратегия обработки элементов состоит в том, чтобы отобразить их — на более высоких уровнях вложенности, по крайней мере — к объектам. Корневые элементы и другие элементы верхнего уровня часто эквивалентны наборам, представленным в Какао объектами NSArray и NSDictionary. Другие элементы могли бы с готовностью отобразиться на один или больше пользовательских объектов модели приложения.

Однако не все элементы лучше всего выражены как объекты. Некоторый более низкий уровень и особенно «листовые» элементы более логически просматриваются как свойства их родительского элемента (если тот элемент отображается на объект). И, конечно, Вы, вероятно, сделали бы фактические атрибуты любого элемента свойством (т.е. переменная экземпляра) соответствующего объекта.

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

Обработка элемента: пример

Пример кода, упомянутый в следующем обсуждении, обрабатывает XML-файл, содержащий персонально-адресную информацию, и преобразовывает ту информацию в объекты Адресной книги (АБПЕРСОН и ABMultipleValue), который может быть добавлен к адресной базе указанного пользователя. Часть XML похожа на следующее:

Перечисление 1  Некоторые демонстрационные XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE addresses SYSTEM "addresses.dtd">
<addresses owner=”swilson”>
    <person>
        <lastName>Doe</lastName>
        <firstName>John</firstName>
        <phone location="mobile">(201) 345-6789</phone>
        <email>jdoe@foo.com</email>
        <address>
            <street>100 Main Street</street>
            <city>Somewhere</city>
            <state>New Jersey</state>
            <zip>07670</zip>
        </address>
    </person>
 
    <!-- more person elements go here -->
 
</addresses>

Давайте посмотрим на то, как могли бы быть обработаны первые три из этих элементов. Когда синтаксический анализатор сначала встречается с этими элементами, он вызывает делегата parser:didStartElement:namespaceURI:qualifiedName:attributes: метод. Для первых двух элементов делегат создает эквивалентный объект. Для третьего элемента (lastName), делегат устанавливает надлежащее свойство второго объекта. Перечисление 2 показывает реализацию делегата для тегов запуска первых трех элементов.

Перечисление 2  Реализовывая parser:didStartElement:namespaceURI:qualifiedName:attribute:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
 
    if ( [elementName isEqualToString:@"addresses"]) {
        // addresses is an NSMutableArray instance variable
       if (!addresses)
             addresses = [[NSMutableArray alloc] init];
        return;
    }
 
    if ( [elementName isEqualToString:@"person"] ) {
        // currentPerson is an ABPerson instance variable
        currentPerson = [[ABPerson alloc] init];
        return;
    }
 
    if ( [elementName isEqualToString:@"lastName"] ) {
        [self setCurrentProperty:kABLastNameProperty];
        return;
    }
    // .... continued for remaining elements ....
}

Делегат идентифицирует элемент, передал в (elementName), затем обрабатывает его соответственно:

Важное действие, предпринятое здесь, имеет путь (переменные экземпляра в этом случае) для отслеживания элемента тока всюду по обходу синтаксического анализатора его. Одной причиной этой важности является семантика parser:foundCharacters:, наиболее вероятно следующий метод делегации вызывается. Этот метод может быть вызван многократно для того же элемента. В этом методе делегат должен добавить символы, переданные в к символам, накопленным до сих пор для элемента. Метод NSMutableString appendString: полезно с этой целью, как показано в Перечислении 3.

  Реализация перечисления 3 parser:foundCharacters:

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if (!currentStringValue) {
        // currentStringValue is an NSMutableString instance variable
        currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
    }
    [currentStringValue appendString:string];
}

Снова код использует переменную экземпляра (currentStringValue) как способ отследить и собрать содержание для элемента тока. Если синтаксический анализатор встречается с некоторыми пробельными символами в содержании элемента, он отправляет сообщение parser:foundIgnorableWhitespace: дать делегату возможность сохранить любые пробельные символы (такие как вкладки или новые строки).

Наконец, когда синтаксический анализатор встречается с конечным тэгом элемента, это вызывает метод делегации parser:didEndElement:namespaceURI:qualifiedName:. Перечисление 4 представляет подход, проявленный делегатом в примере кода.

  Реализация перечисления 4 parser:didEndElement:namespaceURI:qualifiedName:

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    // ignore root and empty elements
    if (( [elementName isEqualToString:@"addresses"]) ||
        ( [elementName isEqualToString:@"address"] )) return;
 
    if ( [elementName isEqualToString:@"person"] ) {
        // addresses and currentPerson are instance variables
        [addresses addObject:currentPerson];
        [currentPerson release];
        return;
    }
    NSString *prop = [self currentProperty];
 
    // ... here ABMultiValue objects are dealt with ...
 
    if (( [prop isEqualToString:kABLastNameProperty] ) ||
        ( [prop isEqualToString:kABFirstNameProperty] )) {
        [currentPerson setValue:(id)currentStringValue forProperty:prop];
    }
    // currentStringValue is an instance variable
    [currentStringValue release];
    currentStringValue = nil;
}

Если делегат решает, что конечный тэг для person элемент, это добавляет объект АБПЕРСОНА к addresses массив и выпуски объект АБПЕРСОНА. Если конечный тэг для lastName элемент (например), делегат использует метод ABRecord setValue:forProperty: установить надлежащее свойство в объекте АБПЕРСОНА (ABRecord является суперклассом АБПЕРСОНА). Наконец, переменная экземпляра, содержащая накопленное содержание для элемента (currentStringValue) выпущен.

Обработка атрибута

addresses элемент, показанный в примере XML в Перечислении 1, включает атрибут:

<addresses owner="swilson">

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

Объект NSXMLParser представляет атрибуты элемента делегату в словаре в заключительном параметре parser:didStartElement:namespaceURI:qualifiedName:attributes:. Перечисление 5 показывает, как делегат в примере обрабатывает owner атрибут.

Перечисление 5  , Обрабатывающее атрибут элемента

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
 
    if ( [elementName isEqualToString:@"addresses"]) {
        // addresses is an NSMutableArray instance variable
        if (!addresses)
            addresses = [[NSMutableArray alloc] init];
        NSString *thisOwner = [attributeDict objectForKey:@"owner"];
        if (thisOwner)
            [self setOwner:thisOwner forAddresses:addresses];
        return;
    // ... continued ...
}}

Делегат извлекает имя пользователя владельца от attributeDict словарь с помощью названия атрибута (owner) как ключ. Это тогда вызывает закрытый метод, связывающий владельца с импортированными данными Адресной книги.