Изменение XML-документа

Можно изменить XML-документ с методами NSXML двумя общими способами:

Эта статья обсуждает оба аспекта модификации XML.

Изменение значений узлов

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

Когда NSXML обрабатывает входной файл или другой источник XML, это автоматически устанавливает значения узлов, поскольку возражает строка. (Это не имеет никакой другой индикации относительно типов значений кроме формы, в которой это находит их: строки.) Хранение значения узлов как строки приемлемо для большинства приложений. Изменение строкового значения является просто вопросом нахождения определенного узла и отправки setStringValue: или setStringValue:resolvingEntities: к нему. Само значение часто прибывает из пользовательского интерфейса, как показано в Перечислении 1.

Перечисление 1  , Устанавливающее строковое значение узла

int index = [form indexOfSelectedItem];
NSXMLNode *node;
switch (index) {
    case 0: // idField
        [[person attributeForName:@"idnum"] setStringValue:
            [idField stringValue]];
        break;
    case 1: // lastName
        node = [[person elementsForName:@"lastName"] objectAtIndex:0];
        if (!node) {
            node = [NSXMLElement elementWithName:@"lastName"];
            [person addChild:node];
        }
        [node setStringValue:[lastName stringValue]];
        break;
// ...

Установка значения узла к объекту кроме объекта NSString может дать Вам определенные преимущества. Как пример, предположите, что Ваше приложение имеет узлы элемента для таких финансовых значений как цена на элемент, количество, налог и общее количество. Путем преобразования значений этих узлов от строк (“45.99”) к числам (45.99), Ваше приложение может выполнить вычисления непосредственно с объектными значениями узлов.

Можно установить любой произвольный объект как объектное значение узла. Когда объект NSXMLNode просят испустить его объектное значение как строку XML, он производит строковое представление значения. Для стандартных атомарных типов это значение находится в канонической форме для типа (как определено спецификацией типов данных схемы XML W3C). Для использования в своих интересах этого поведения по умолчанию объект, который Вы устанавливаете, должен быть экземпляром Фундаментального класса, соответствующего одному из атомарных типов в модели данных XQuery. Таблица 1 перечисляет эти классы и их соответствующие типы.

Таблица 1  Эквивалентные классы и атомарные типы

Фундаментальный класс

Атомарный тип

NSString

строка (использование setObjectValue:)

NSNumber

целое число, десятичное число, плавание, дважды, булевская переменная

NSCalendarDate

дата

NSData

base64 и шестнадцатеричный двоичный файл

NSURL

URI

NSArray

NMTOKENS, IDREFS, ENTITIES

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

Необходимо установить объектное значение каждого узла в документе сами — NSXML не делает этого для Вас автоматически. Обычно Вы сначала установили бы объектные значения глобально после того, как NSXML обрабатывает источник XML и производит древовидное представление документа. Можно обойти дерево или иначе искать узлы документа, значения которого Вы хотите установить в объекты. После того при создании новых узлов можно установить их объектные значения в той точке.

Фрагмент кода в Перечислении 2 использует метод источника данных NSTableView tableView:objectValueForTableColumn:row: как точка, в которой можно установить объектные значения узлов атрибута, представляющих даты. Строковые значения этих атрибутов должны соответствовать образцу «mm/dd/yy», где «мм» является числом месяца, «dd» является днем месяца, и «yy» является последними двумя цифрами года. (Можно присоединить объект NSDateFormatter к полю ввода, чтобы гарантировать, чтобы недавно вводимые даты соответствовали этому образцу также.) Фрагмент кода использует метод NSCalendarDate dateWithString:calendarFormat: произвести преобразование из строкового значения для возражения значению.

Перечисление 2  , Устанавливающее объектное значение на основе строкового образца

// in tableView:objectValueForTableColumn:row:...
 if ([columnID isEqualToString:@"Hire Date"]) {
        NSXMLNode *attrNode = [[employeeArray objectAtIndex:rowIndex]
            attributeForName:@"hireDate"];
        if (attrNode) {
            if (![[attrNode objectValue] isKindOfClass:
                [NSCalendarDate class]]) {
                NSCalendarDate *date = [NSCalendarDate dateWithString:
                    [attrNode stringValue] calendarFormat:@"%m/%d/%y"];
                [attrNode setObjectValue:date];
            }
            return [attrNode objectValue];
        }
    }
// ...

Один раз объектные значения hireDate атрибуты были установлены, приложение может пойти о сравнении дат, вычислительные периоды между датами и чем-либо еще, что может быть сделано с методами NSCalendarDate и классов NSDate. Перечисление 3 является методом, использующим значения объекта NSCalendarDate для идентификации сотрудника, работавшего самое длинное на гипотетическую компанию.

Перечисление 3  Используя объектное значение атрибута

- (NSXMLElement *)earliestHire {
 
    NSXMLElement *earliest;
    int i;
    NSError *err;
    NSArray *nodes = [xmlDoc nodesForXPath:@".//employee" error:&err];
    if (!nodes || [nodes count] == 0)
        return nil;
    earliest = [nodes objectAtIndex:0];
    for (i = 1; i < [nodes count]; i++) {
        NSXMLElement *current = [nodes objectAtIndex:i];
        id date1 = [[earliest attributeForName:@"hireDate"] objectValue];
        id date2 = [[current attributeForName:@"hireDate"] objectValue];
        if ([date1 isKindOfClass:[NSDate class]] &&
            [date2 isKindOfClass:[NSDate class]] ) {
            if ([(NSDate *)date2 compare:(NSDate *)date1]
                        == NSOrderedAscending) {
                earliest = current;
            }
        }
    }
    return earliest;
}

Добавление, удаляя и перемещая узлы

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

Таблица 2  методы NSXML для управления узлами

Метод

Объявленный в классах

detach

NSXMLNode

addChild:

NSXMLElement, NSXMLDocument и NSXMLDTD

setChildren:

NSXMLElement, NSXMLDocument и NSXMLDTD

insertChild:atIndex:

NSXMLElement, NSXMLDocument и NSXMLDTD

insertChildren:atIndex:

NSXMLElement, NSXMLDocument и NSXMLDTD

removeChildAtIndex:

NSXMLElement, NSXMLDocument и NSXMLDTD

replaceChildAtIndex:withNode:

NSXMLElement, NSXMLDocument и NSXMLDTD

detach метод отличается от других в этом списке в этом, Вы вызываете его на сам дочерний узел а не родителя узла. Можно отправить detach к любому объекту NSXMLNode (не только те позволили иметь дочерние элементы) повредить соединение между тем узлом и его родителем. Как только узел отсоединяется от его родителя, Вы свободны повторно прикрепить его к другому родителю в другом месте в дереве с помощью одного из методов для вставки, добавления или замены дочерних узлов.

Все другие методы в Таблице 2 отправлены в родителя добавляемого узла, удалены или заменены. Родитель должен быть объектом, который является экземпляром NSXMLElement, NSXMLDocument или класса NSXMLDTD. Некоторые из этих классов устанавливают ограничения для того, какие виды дочерних элементов могут быть добавлены, в зависимости от класса родителя:

Методы обработки узла этих классов идентичны. И они являются прямыми с их надлежащими использованиями, сделанными однозначными их именами. Они могут потребовать, чтобы Вы сделали одну или более вещей прежде, чем вызвать их:

Наиболее распространенный получатель сообщений манипулирования узла является экземпляром класса NSXMLElement. Следующие примеры кода иллюстрируют, как методы обработки узла NSXMLElement могли бы использоваться в Вашем приложении. Перечисление 4 включает код, создающий вложенные элементы (внутренний, содержащий атрибут) со следующей строковой формой XML:

<Figure><Graphic href="../Art/cc_finalui.gif"></Graphic>

Перечисление 4  , Добавляющее, вложило элементы

// Note: relativePathToFilename is a custom method
NSString *relativePathToArt = [[self fileName]
    relativePathToFilename:selectedFile];
 
NSXMLElement *newFigureElement = [NSXMLElement elementWithName:@"Figure"];
NSXMLElement *newGraphicElement = [NSXMLElement elementWithName:@"Graphic"];
NSXMLNode *pathToHrefAttribute = [NSXMLNode attributeWithName:@"href"
    stringValue:relativePathToArt];
 
[newGraphicElement addAttribute:pathToHrefAttribute];
[newFigureElement addChild:newGraphicElement];
 
NSXMLElement *selectedParent = [treeView selectedItem];
[selectedParent addChild:newFigureElement];
// ...

Перечисление 5 является методом, заменяющим дочерний узел текстовым узлом, содержащим строковое значение дочернего элемента.

Перечисление 5  , Заменяющее элемент

- (void)unwrapElement:(NSXMLElement *)childToUnwrap {
 
    id parent = [childToUnwrap parent];
    int indexOfChildToUnwrap = [childToUnwrap index];
    NSXMLNode *textNode = [NSXMLNode textWithStringValue:[childToUnwrap stringValue]];
 
    // Unwrapping is defined as replacing the selected element
    // with a text representation of itself.
    [parent replaceChildAtIndex:indexOfChildToUnwrap withNode:textNode];
}

Как Вы добавляете и перемещаете узлы, можно иногда хотеть проверить документ, чтобы проверить, что изменения соответствуют управляющей схеме. Для проверки документа отправьте validateAndReturnError: обменивайтесь сообщениями к объекту NSXMLDocument. Если работа не допустима, возвраты метода NO; причины недействительности возвращаются косвенно в параметре метода NSError.