Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
46
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

2.9. Работа с xml-документами в .Net framework

Рассмотрим вопросы, связанные с чтением и записью XML-документов. Для этих целей предназначен набор классов из пространства имен System.Xml. Упомянем такие классы, как XmlDocument, XmlTextReader, XmlTextWriter, XmlValidatingReader.

Класс XmlReader – это абстрактный класс, предназначенный для чтения xml-данных из потока. В .NET Framework существуют три класса, построенных на основе XmlReader: XmlTextReader (разбор xml-данных на основе текстового потока), XmlNodeReader (разбор XML из набора объектов XmlNode) и XmlValidatingReader (при чтении производится проверка документа с использованием DTD и/или Shema).

Обычно для простого чтения XML достаточно класса XmlTextReader. Конструктор класса может быть вызван в различных формах. В следующем примере предполагается читать данные из файла Students.xml:

XmlTextReader tr = new XmlTextReader("Students.xml");

Объект класса XmlTextReader может быть построен на основе потоков:

XmlTextReader tr = new XmlTextReader(

new TextReader(

new FileStream("Students.xml",

FileMode.Open)));

Как видим, XmlTextReader может извлекать данные из любых потоков, включая сетевые потоки и потоки, связанные с базами данных. XML-данные можно извлекать и из потоков, сконструированных на основе строк:

string xmlContent =

"<book>" +

" <title>Чук и Гек</title>" +

" <author>Аркадий Гайдар</author>" +

"</book>";

XmlTextReader tr = new XmlTextReader(

new StringReader(xmlContent));

После создания объект XmlTextReader извлекает xml-конструкции из потока при помощи метода Read(). Тип текущей конструкции (элемент, атрибут) можно узнать, используя свойство NodeType, значениями которого являются элементы перечисления XmlNodeType. С конструкцией можно работать, используя различные свойства, такие как Name (возвращает имя элемента или атрибута), Value (возвращает данные элемента) и так далее.

В таблице 16 приведены все возможные значения перечисления XmlNodeType.

Таблица 16

Значения перечисления XmlNodeType

Значение

Пример

Attribute

Department="Informatics"

CDATA

<![CDATA["This is character data"]]>

Comment

<!-- This is a comment -->

Document

<Students>

DocumentType

<!DOCTYPE Students SYSTEM " Students.dtd">

Element

<student>

EndElement

</student>

Entity

<!ENTITY filename "Strats.xml">

EntityReference

<

Notation

<!NOTATION GIF89a SYSTEM "gif">

ProcessingInstruction

<?perl lower-to-upper-case ?>

Text

Petrov

Whitespace

<Make/>\r\n<Model/>

XmlDeclaration

<?xml version="1.0"?>

Дадим некоторые комментарии. Значения DocumentType, Entity, EntityReference и Notation связаны с блоком определения типа документа (document type definition, DTD). Напомним, что при помощи подобного блока, который может являться частью XML-документа, задается приемлемая схема документа. Значение Whitespace представляет пустое (вернее, не обрабатываемое) пространство между тэгами.

Следующий пример демонстрирует разбор xml-файла и вывод разобранных конструкций на экран:

using System;

using System.Xml;

class XmlFun {

static void Main() {

XmlTextReader rdr = new XmlTextReader("Students.xml");

// Разбираем файл и выводим на консоль каждый элемент

while(rdr.Read()) {

// Реагируем в зависимости от NodeType

// Не все значения NodeType отслеживаются!!!

switch(rdr.NodeType) {

case XmlNodeType.Element:

Console.Write("<{0}>", rdr.Name);

break;

case XmlNodeType.Text:

Console.Write(rdr.Value);

break;

case XmlNodeType.CDATA:

Console.Write("<![CDATA[{0}]]>", rdr.Value);

break;

case XmlNodeType.Comment:

Console.Write("<!--{0}-->", rdr.Value);

break;

case XmlNodeType.XmlDeclaration:

Console.Write("<?xml version='1.0'?>");

break;

case XmlNodeType.EndElement:

Console.Write("</{0}>", rdr.Name);

break;

}

}

}

}

Набор методов класса XmlTextReader вида MoveXXX(), таких как MoveToNextElement(), может использоваться для извлечения соответствующей конструкции из потока. Методы используются для перехода к следующей конструкции, вернуться к просмотренным конструкциям нельзя.

Подобно тому, как класс XmlReader является абстрактным классом для чтения xml-данных из потока, класс XmlWriter – это абстрактный класс для создания xml-данных. Подчеркнем, что xml-данные всегда могут быть созданы при помощи простой строки и затем записаны в любой поток:

string xmlContent = "<greeting>" + message + "</greeting>";

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

using System;

using System.Xml;

class XmlFun {

static void Main() {

string message = "Hello, dude!";

XmlTextWriter xw = new XmlTextWriter("greetings.xml",

System.Text.Encoding.UTF8);

xw.Formatting = Formatting.Indented;

xw.Indentation = 2;

xw.WriteStartDocument();

xw.WriteStartElement("greeting");

xw.WriteString(message);

xw.WriteEndElement();

xw.WriteEndDocument();

xw.Flush();

}

}

Класс XmlDocument представляет XML-документ в виде дерева узлов-элементов. Каждый узел является экземпляром класса XmlNode, который содержит методы и свойства для навигации по дереву, чтения и записи информации узла и другие.

Пусть имеется следующий файл Students.xml:

<?xml version="1.0" encoding="UTF-8"?>

<Students>

<student Department="Informatics">

<name>Ivanov</name>

<year>1980</year>

</student>

<student>

<name>Petrov</name>

<year>1980</year>

<ball>5.0</ball>

</student>

<student Department="Informatics">

<name>Sidorova</name>

<year>1981</year>

<ball>4.5</ball>

</student>

</Students>

Следующий код создает объект класса XmlDocument, и этот объект загружается информацией из файла Students.xml:

XmlDocument doc = new XmlDocument();

doc.Load("Students.xml");

Метод Load() считывает документ и разбирает его в памяти на элементы. Если документ не является правильно форматированным, генерируется исключение XmlException.

За успешным вызовом метода Load() обычно следует обращение к свойству DocumentElement объекта, представляющего документ. Данное свойство возвращает ссылку на объект класса XmlNode. Этот объект позволяет обнаружить у элемента дочерние элементы (свойство HasChildNodes) и получить к ним доступ, используя свойство ChildNodes, являющееся коллекцией типа XmlNodeList. Комбинация свойств HasChildNodes и ChildNodes позволяет использовать при обработке XML-документов рекурсивный подход:

using System;

using System.Xml;

class MainClass {

public static void Main() {

XmlDocument doc = new XmlDocument();

doc.Load("Students.xml");

OutputNode(doc.DocumentElement);

}

public static void OutputNode(XmlNode node) {

Console.WriteLine("Type={0} \t Name={1} \t Value={2}",

node.NodeType, node.Name, node.Value);

// Если есть дочерние элементы,

// рекурсивно обрабатываем их

if(node.HasChildNodes) {

XmlNodeList children = node.ChildNodes;

foreach(XmlNode child in children)

OutputNode(child);

}

}

}

Приведенный код не выводит значения атрибутов элемента. Свойство ChildNodes не включает атрибуты. Все атрибуты элемента хранятся в свойстве Attributes и представлены типом XmlAttribute. Следующий фрагмент кода позволяет обработать атрибуты элемента:

void OutputNode(XmlNode node) {

. . .

if(node.Attributes != null){

foreach (XmlAttribute attr in node.Attributes)

Console.WriteLine("Type={0}\tName={1}\tValue={2}",

attr.NodeType, attr.Name, attr.Value);

}

. . .

}

У объекта класса XmlNode имеются свойства NodeType, Name и Value. Следует иметь в виду, что обращаться к Name и Value нужно в зависимости от значения NodeType. У элементов (XmlNodeType.Element) нет Value, но определено Name. У текста (XmlNodeType.Text) определено Name, но нет Value. У атрибутов определены и Name, и Value.

Для того чтобы получить определенный узел или набор узлов, нет необходимости итеративно просматривать весь XML-документ. Для этих целей можно использовать методы класса XmlDocument такие как GetElementsByTagName(), SelectNodes(), SelectSingleNode().

При помощи класса XmlDocument можно не только читать, но и создавать XML-документы. В следующем примере загружается файл Students.xml, удаляется его первый элемент (первый студент), добавляется еще один элемент, описывающий студента, и результат сохраняется в файл Students_new.xml.

using System;

using System.Xml;

class XmlWork {

static void Main() {

XmlDocument doc = new XmlDocument();

// Загружаем информацию из файла

doc.Load("Students.xml");

// Создаем необходимые элементы

XmlNode student = doc.CreateElement("student");

XmlNode name = doc.CreateElement("name");

XmlNode year = doc.CreateElement("year");

XmlNode ball = doc.CreateElement("ball");

// Создаем текстовое наполнение элементов

XmlNode text1 = doc.CreateTextNode("Mr. Zorg");

XmlNode text2 = doc.CreateTextNode("1990");

XmlNode text3 = doc.CreateTextNode("4.7");

// Связываем элементы и текстовое наполнение

name.AppendChild(text1);

year.AppendChild(text2);

ball.AppendChild(text3);

// Связываем элементы в одну структуру

student.AppendChild(name);

student.AppendChild(year);

student.AppendChild(ball);

// Получаем корневой элемент документа

XmlNode root = doc.DocumentElement;

// Удаляем первый дочерний элемент

root.RemoveChild(root.FirstChild);

// Добавляем сконструированный нами элемент

root.AppendChild(student);

// Сохраняем все в файл

doc.Save("Students_new.xml");

}

}

Другие методы класса XmlDocument, которые могут быть полезны при модификации XML-документа, это методы PrependChild(), InsertBefore(), InsertAfter(), RemoveAll(), ReplaceChild(). Для конструирования элемента можно использовать свойство XmlNode.InnerText, которое должно содержать подходящую строку, представляющую содержимое элемента:

XmlNode student = doc.CreateElement("student");

student.InnerText = "<name>Mr. Zorg</name>" +

"<year>1990</year>" +

"<ball>4.7</ball>";

Класс XmlValidatingReader позволяет производить проверку допустимости XML-документов заданной XSD-схеме или описанию DTD. Объект класса XmlValidatingReader присоединяется к объекту XmlTextReader (или XmlNodeReader, если нужно проверить лишь часть документа). При чтении документа и возникновении ошибки допустимости генерируется событие ValidationEventHandler, с передачей информации о позиции ошибки в документе. Далее рассмотрен пример использования класса XmlValidatingReader.

// Создаем ридер для некого XML-документа

XmlTextReader xtr = new XmlTextReader("Students.xml");

// Объект XmlValidatingReader порождаем на основе ридера

XmlValidatingReader xvr = new XmlValidatingReader(xtr);

// Задаем тип проверки допустимости.

// В данном случае --- XSD-схема

xvr.ValidationType = ValidationType.Schema;

// Так как эта схема не храниться в самом XML-документе,

// необходимо присоединить ее к XmlValidatingReader.

// Создем коллекцию схем (правда, из одного элемента)

XmlSchemaCollection xsc = new XmlSchemaCollection();

xsc.Add("","data.xsd");

// Добавляем коллекцию к XmlValidatingReader

xvr.Schemas.Add(xsc);

// Назначаем обработчик события на ошибки

xvr.ValidationEventHandler +=

new ValidationEventHandler(ValidationError);

Хотя класс XmlValidatingReader предназначен для работы с XmlTextReader, следующий код демонстрирует, как использовать его с XmlDocument.

XmlDocument doc = new XmlDocument();

XmlTextReader tr = new XmlTextReader("Sample.xml");

XmlValidatingReader reader = new XmlValidatingReader(tr);

doc.Load(reader);

В пространстве имен System.Xml.XPath определен класс XPathNavigator. Этот класс используется для перемещения по XML-документу или для запроса содержимого документа с помощью выражения XPath. Объект класса XPathNavigator создается вызовом метода CreateNavigator() у объектов классов XmlDocument, XPathDocument, XmlDataDocument. Методы класса XPathNavigator позволяют перемещаться по XML-документу, либо делая выборки узлов (группа методов Select()) либо получая очередной элемент, атрибут и т.д. (группа методов MoveToXXX()). Если производится выборка узлов, то они доступны через итератор XPathNodeIterator.

// Считываем XML-файл при помощи объекта XPathDocument

XPathDocument document = new XPathDocument("books.xml");

// Создаем навигатор

XPathNavigator navigator = document.CreateNavigator();

// Выбираем определенные узлы документа

XPathNodeIterator nodes = navigator.Select("/bookstore/book");

while(nodes.MoveNext()) {

Console.WriteLine(nodes.Current.Name);

}