Работа с XML в .NET

Кондратьев Денис

XML это язык разметки, с помощью которого можно описать произвольные данные. На основе XML можно организовать хранение информации и ее обмен, который не зависит от ни от конкретных приложений, ни от платформы, на которой они исполняются. На основе XML построены web-службы. XML широко применяется в web-приложениях для отделения данных от их отображения. Его стандарт утвержден World Wide Web Consortium (W3C) в 1998 году.    Для работы с XML применяются XML-парсеры. Существует два основных типа парсеров: Simple API for XML (SAX) и Document Object Model (DOM). SAX основан на курсорах и событиях, возникающих при проходе по узлам XML документа. SAX-прасеру не требуется большого количества памяти для разбора даже больших документов (т.к. ему не нужно загружать в память весь документ), но его существенным ограничением является то, что можно перемещаться по документу только в одном направлении. DOM полностью загружает документ в память и представляет его в виде дерева, поэтому можно произвольно перемещаться по XML-документу.

    Многие составляющие технологии .NET неразрывно связаны с XML. А значит, XML хорошо поддерживается со стороны Framework Class Library. Классы для работы с XML собраны в пространстве имен System.Xml. В .NET поддерживаются следующие технологии

  • XML 1.0
  • пространства имен XML
  • XSD схемы
  • выражения XPath
  • XSL преобразования
  • DOM Level 1 Core, DOM Level 2 Core

   В этой статье рассматриваются приемы работы с XML в .NET на примере небольшого windows-forms приложения для работы с заказами товаров. Данные о заказах и товарах хранятся в сущностных классах Order и Good. Приложение позволяет создавать новые заказы и добавлять в них товары, сохранять и загружать данные о заказах из XML файла и преобразовывать структуру XML-файла для передачи другой организации. Каждый заказ имеет дату и адрес доставки, а каждый товар - название и стоимость. Заказы и товары в заказах на форме приложения отображаются в виде дерева.

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

    Для создания новых XML-документов применяется класс XmlTextWriter. Он обеспечивает быстрое небуферизованное создание XML-документов  и их запись в файлы, потоки, на консоль и пр. Рассмотрим функцию для сохранения в файл данных о заказах.

private void menuItemSave_Click(object sender, System.EventArgs e)
{
    SaveFileDialog saveDlg = new SaveFileDialog();
    saveDlg.FileName = "заказы";
    saveDlg.DefaultExt = "xml";
    saveDlg.Filter = "Файлы XML (*.xml)/*.xml";
    if (saveDlg.ShowDialog() != DialogResult.OK)
        return;

    XmlTextWriter writer = null;

    try
    {
         writer = new XmlTextWriter(saveDlg.FileName, System.Text.Encoding.Unicode);

         writer.WriteStartDocument();
         writer.WriteStartElement("Заказы");

         // сохраняем заказы
         foreach (Order order in orders)
         {
              writer.WriteStartElement("Заказ");
              writer.WriteAttributeString("Адрес", order.Address);
              writer.WriteAttributeString("Дата", order.Date.ToShortDateString());

              // сохраняем товар
              foreach (Good good in order.Goods)
              {
                   writer.WriteStartElement("Товар");
                   writer.WriteAttributeString("Название", good.Name);
                   writer.WriteAttributeString("Цена", good.Price.ToString());
                   writer.WriteEndElement();
              }

              writer.WriteEndElement();
         }

         writer.WriteEndElement();
         writer.WriteEndDocument();
    }
    catch (Exception ex)
    {
         MessageBox.Show("Ошибка: " + ex.Message);
    }
         finally
    {
         if (writer != null)
              writer.Close();
    }
}

    Создавая объект XmlTextWriter в его конструктор мы передали имя файла, в который будут сохраняться XML-данные и кодировку. Перегруженная версия конструктора принимает вместо имени файла объект Stream. Таким образом, в конструктор можно передать, например, объект MemoryStream, а потом сохранить XML-данные в БД. Вызов метода WriteStartDocument() записывает строку с объявлением версии XML и типом кодировки. В нашем случаем это будет
.
    Пары функций WriteStartElement() и WriteEndElement() записывают начало и конец тега с указанным названием. В массиве orders хранятся объекты Order, инкапсулирующие заказы. В цикле мы перебираем все заказы и для каждого заказа вызываем метод WriteStartElement с параметром "Заказ". Для создания корректного XML-документа каждому вызову функции WriteStartElement() должен соответствовать вызов WriteEndElement(). Перегруженные версии функции WriteStartElement() позволяют указать для тега пространство имен и префикс. Для указания атрибута тега применяется функция WriteAttributeString(). С помощью перегруженных версий функции также можно указать префикс и пространство имен для атрибута. Функция WriteEndDocument() применяется для защиты от случайных ошибок создания XML-документа - она закрывает все открытые теги и атрибуты и переводит writer в начальное состояние. Сохраним следующие заказы.

Image 

В результате у нас получается документ такого вида


<Заказы>
    <Заказ Адрес="Уфа" Дата="21.04.2004">
        <Товар Название="Товар_А" Цена="100" />
        <Товар Название="Товар_Б" Цена="150" />
        <Товар Название="Товар_В" Цена="370" />
     
    <Заказ Адрес="Москва" Дата="24.04.2004">
        <Товар Название="Товар_Г" Цена="400" />
   
    <Заказ Адрес="Омск" Дата="28.04.2004">
        <Товар Название="Товар_Д" Цена="255" />
    

    Класс XmlTextWriter предоставляет большое количество методов для создания XML-документов. Например, функция WriteElementString() создает элемент, содержащий одно текстовое значение, такое как <Дата>01.05.04. Функция WriteBinHex() массив байтов в шеснадцатиричном виде, а функция WriteComment() вставляет комментарий.

Чтение документов с XmlTextReader

    Класс XmlTextReader обеспечивает быстрое однонаправленное чтение потока XML-данных. Данные могут быть получены из файла, объекта потока Stream или объекта TextReader. XmlTextReader обычно применяется если нужно считать XML документ и получить из него данные. Так как XmlTextReader не загружает весь документ в память, он является наилучшим выбором при обработке больших XML файлов - логов, дампов БД и пр.
    Прочитаем данные о заказах с помощью класса XmlTextReader из XML-файла, созданного нашей программой в предыдущем разделе.

OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Файлы XML (*.xml)/*.xml"; if (dlg.ShowDialog() ! = DialogResult.OK)
    return;

XmlTextReader reader = null;
orders.Clear();

try
{
    reader = new XmlTextReader(dlg.FileName);
    reader.WhitespaceHandling = WhitespaceHandling.None; // пропускаем пустые узлы

    while (reader.Read())
        if (reader.NodeType == XmlNodeType.Element)
            if (reader.Name == "Заказ")
            {
                 Order order = new Order(reader.GetAttribute("Адрес"), DateTime.Parse(reader.GetAttribute("Дата"))); 

                 // получаем товары в заказе
                 while (reader.Read() && reader.Name == "Товар")
                     order.AddGood(reader.GetAttribute("Название"), float.Parse(reader.GetAttribute("Цена")));
                 orders.Add(order);
            }

            ShowOrders();
}
catch (Exception ex)
{
    MessageBox.Show("Ошибка: " + ex.Message);
}
finally
{
    if (reader != null)
        reader.Close();
}

    XML-данные, которые читает XmlTextReader, берутся из файла, выбранного пользователем в диалоге. Для подавления пустых строк мы устанавливаем значение None для свойства WhitespaceHandling. Метод Read() производит чтение из потока следующего узла XML-документа. Он возвращает true если удалось считать узел. Обязательно нужно вызвать метод Read перед первым обращением к данным, т.к. в момент инициализации XmlTextReader не содержит никаких данных.
    Имя тега возвращает свойство Name. В момент, когда имя текущего тега "Заказ", мы создаем новый объект Order. Дату и адрес заказа мы получаем с помощью метода GetAttribute. Вложенные в заказ товары получаем перебирая теги в цикле while до тех пор, пока имя текущего тега "Товар". Важно то, что если запрашиваемый атрибут не найден, то возвращается пустая строка. При этом проверка того, является ли этот атрибут обязательным для текущего тега не производится. При чтении файла bad.xml, в одном из заказов не указан адрес. Однако, программа успешно считывает файл и выводит такие данные. Проверку допустимости XML-документа осуществляет класс XmlValidatingReader, о котором будем говорить ниже.

Image

 


Страница сайта http://silicontaiga.ru
Оригинал находится по адресу http://silicontaiga.ru/home.asp?artId=6224