"C# для профессионалов. Том II" - читать интересную книгу автора (Робинсон Симон, Корнес Олли, Глинн Джей,...)
Объектная модель документа в .NET
Реализация объектной модели документа (DOM, Document Object Model) в .NET поддерживает спецификации W3C DOM Level 1 и Core DOM Level 2. DOM реализуется с помощью класса XmlNode. XmlNode является абстрактным классом, который представляет узел документа XML. XmlNodeList является упорядоченным списком узлов. Это живой список узлов, и любые изменения в любом узле немедленно отражаются в списке. XmlNodeList поддерживает индексный доступ или итеративный доступ. Эти два класса составляют основу реализации DOM на платформе .NET. Вот список классов, которые основываются на XmlNode.
Имя класса
Описание
XmlLinkedNode
Расширяет XmlNode. Возвращает узел непосредственно перед или после текущего узла. Добавляет свойства NextSibling и PreviousSibling в XmlNode.
XmlDocument
Расширяет XmlNode. Представляет весь документ. Реализует спецификации DOM Level 1 и Level 2.
XmlAttribute
Расширяет XmlNode. Объект атрибута объекта XmlElement.
XmlCDataSection
Расширяет XmlCharacterData. Объект, который представляет раздел документа CData.
XmlCharacterData
Абстрактный класс, который предоставляет методы манипуляции с текстом для других классов. Расширяет XmlLinkedNode.
XmlComment
Расширяет XmlCharacterData. Представляет объект комментария XML.
XmlDeclaration
Расширяет XmlLinkedNode. Представляет узел объявления (lt;?xml version='1.0' ...gt;)
XmlDocumentFragment
Расширяет XmlNode. Представляет фрагмент дерева документа.
XmlDocumentType
Расширяет XmlLinkedNode. Данные, связанные с объявлением типа документа.
XmlElement
Расширяет XmlLinkedNode. Объект элемента XML.
XmlEntity
Расширяет XmlNode. Синтаксически разобранный или неразобранный узел сущности.
XmlEntityReferenceNode
Расширяет XmlLinkedNode. Представляет ссылочный узел сущности
XmlNotation
Расширяет XmlNode. Содержит нотацию, объявленную в DTD или в схеме.
XmlProcessingInstruction
Расширяет XmlLinkedNode. Содержит инструкцию обработки XML.
XmlSignificantWhitespace
Расширяет XmlCharacterData. Представляет узел с разделителем. Узлы создаются, только если флаг PreserveWhiteSpace задан как true.
XmlWhitespace
Расширяет XmlCharacterData. Представляет разделитель в содержимом элемента. Узлы создаются, только если флаг PreserveWhiteSpace задан как true.
XmlText
Расширяет XmlCharacterData. Текстовое содержимое элемента или атрибута.
Как можно видеть .NET делает доступным класс, соответствующий почти любому типу XML. Мы не будем рассматривать каждый класс подробно, но разберем несколько примеров. Вот как выглядит диаграмма наследования:
Первый пример будет создавать объект XmlDocument, загружать документ с диска и загружать окно списка с данными из элементов title. Это аналогично одному из примеров, которые были выполнены в разделе XmlReader. Отличие заключается в том, что осуществляется выбор, с какими узлами мы хотим работать, вместо того чтобы использовать весь документ. Вот код для выполнения этого в среде XmlNode. Посмотрите, как просто он выглядит при сравнении (файл можно найти в папке DOMSample1 загруженного архива):
foreach(XmlNode node in nodeLst) listBox1.Items.Add(node, InnerText);
}
Обратите внимание, что мы добавили следующее объявление на уровне модуля:
private XmlDocument doc=new XmlDocument();
Если бы это было все, что нужно делать, то использование XmlReader было бы значительно более эффективным способом загрузки окна списка. Причина в том, что мы прошли через документ один раз и затем закончили с ним работу. Однако, если желательно повторно посетить узел, то использование XmlDocument является лучшим для этого способом. Слегка расширим пример (новая версия находится в DOMSample2):
if (foundNode!=null) MessageBox.Show(foundNode.InnerText);
else MessageBox.Show("Not found");
}
В этом примере listbox с заголовками загружается из документа books.xml. Когда мы щелкаем на окне списка, вызывая порождение события SelectedIndexChange (не забудьте добавить код, присоединяющий обработчик событий в функцию InitializeComponent), мы берем текст выбранного пункта в listbox, в данном случае заголовок книги, создаем оператор XPath и передаем его в метод SelectSingleNode объекта doc. Он возвращает элемент book, частью которого является title (foundNode). Выведем для наглядности InnerText узла в окне сообщения. Мы можем продолжать щелкать на элементах в listbox сколько угодно раз, так как документ загружен и остается загруженным, пока мы его не освободим.
Небольшой комментарий в отношении метода SelectSingleNode. Это реализация XPath в классе XmlDocument. Существуют методы SelectSingleNode и SelectNodes. Оба они определены в XmlNode, на котором основывается XmlDocument. SelectSingleNode возвращает XmlNode, и SelectNodes возвращает XmlNodeList. Пространство имен System.Xml.XPath содержит более насыщенную реализацию XPath (см. ниже).
Ранее рассматривался пример XmlTextWriter, который создает новый документ. Ограничение состояло в том, что он не вставлял узел в текущий документ. Это можно сделать с помощью класса XmlDocument. Если изменить button1_Click из предыдущего примера, то получим следующий код (DOMSample3):
if (foundNode!=null) MessageBox.Show(foundNode.InnerText);
else MessageBox.Show("Not found");
}
При выполнении этого кода будет получена функциональность предыдущего примера, но в окне списка появилась одна дополнительная книга "The Case of Missing Cookie". Щелчок мыши на заголовке этой книги приведет к выводу такой же информации, как и для других книг. Анализируя код, можно увидеть, что это достаточно простой процесс. Прежде всего создается новый элемент book:
XmlElement newBook = doc.CreateElement("book);
Метод CreateElement имеет три перегружаемые версии, которые позволяют определить имя элемента, имя и пространство имен URI, и, наконец, prefix (префикс), lоcalname (локальное имя) и namespace (пространство имен). Когда элемент создан, необходимо добавить атрибуты
newBook.setAttribute("genre", "Mystery");
newBook.SetAttribute("publicationdate", "2001");
newBook.SetAttribute("ISBN", "123456789");
Напомним, что класс XmlAttribute расширяет класс XmlNode, поэтому нам доступны все свойства и методы XmlNode. Даже если имеется очень сложная структура, то при ее размещении никаких проблем возникать не должно.
Теперь, когда атрибуты созданы и необходимо добавить другие элементы книги:
XmlElement newTitle=doc.CreateElement("title");
newTitle.InnerText="Case of the Missing Cookie";
newBook.AppendChild(newTitle);
Здесь снова создается новый объект на основе XmlElement (newTitle). Присваиваем свойству InnerText заголовок новой книги и добавляем потомок к элементу book. Затем это повторяется для остальных элементов book. Отметим, что элемент name добавлен как потомок элемента author. Это дает нам правильное отношение вложенности.
Наконец, мы добавляем элемент newBook к узлу doc.DocumentElement. Это тот же уровень, что и у всех других элементов book. Мы заменили существующий документ новым, в отличие от XmlWriter, где можно было только создать новый документ. Последнее, что нужно сделать, это записать новый документ XML на диск. В этом примере мы создаем новый XmlTextWriter и передаем его в метод WriteContentTo. Не забудьте вызвать метод Close на XmlTextWriter, чтобы сбросить содержимое внутренних буферов и закрыть файл. Методы WriteContentTo и WriteTo получают XmlTextWriter в качестве параметра. WriteContentTo сохраняет текущий узел и всех потомков в XmlTextWriter, в то время как WriteTo сохраняет текущий узел. Так как doc является объектом на основе XmlDocument, он представляет весь документ и поэтому будет сохранен. Можно было бы также использовать метод Save. Он всегда будет сохранять весь документ. Save имеет четыре перегружаемые версии. Можно определить строку с именем файла и путем доступа, объект на основе класса Stream, объект на основе класса TextWriter, или объект на основе XmlWriter. Именно это было использовано при выполнении примера. Отметим новую запись в конце списка:
Если нужно создать документ с самого начала, можно использовать класс XmlTextWriter. Можно также использовать XmlDocument. Какой из них выбрать? Если данные, которые желательно поместить в XML, доступны и готовы для записи, то самым подходящий будет класс XmlTextWriter. Однако, если необходимо создавать документ XML постепенно, вставляя узлы в различные места, то наиболее приемлемым будет применение XmlDocument. Вот тот же пример, который только что был рассмотрен, но вместо редактирования текущего документа мы создаем новый документ (DOMSample4):
Сначала создается новый объект XmlDeclaration. Параметрами являются версия (в настоящее время всегда "1.0"), кодировка (null подразумевает UTF-8) и, наконец, флаг standalone. Он может быть yes или no, но если вводится null или пустая строка, как в нашем случае, этот атрибут не будет добавляться при сохранении документа. Параметр кодировки должен задаваться строкой, которая является частью класса System.Text.Encoding, если не используется null.
Следующим создаваемым элементом станет DocumentElement. В данном случае мы называем его newBookstore, чтобы можно было видеть различие. Остальная часть кода является такой же, как и в предыдущем примере, и работает точно так же. Вот файл booksEdit.xml, создаваемый этим кодом:
Мы не рассмотрели всех особенностей класса XmlDocument или других классов, способствующих созданию модели DOM в .NET. Однако мы видели мощь и гибкость, которые предлагает реализация DOM в .NET. Класс XmlDocument обычно используется, когда требуется случайный доступ к документу. Используйте классы на основе XmlReader, когда желательна модель потокового типа. Помните, что гибкость XmlDocument на основе XmlNode обеспечивается более высокими требованиями к памяти, поэтому подумайте тщательно о том, какой метод предпочтительнее в конкретной ситуации.