Трансформации XML

55

С помощью LINQ to XML можно выполнять трансформации XML, используя для этого два совершенно разных подхода. Первый подход заключается в применении языка XSLT (extensible Stylesheet Language Transformations) через классы-мосты — XmlReader и XmlWriter. Второй подход предусматривает использование для трансформаций самого API-интерфейса LINQ to XML за счет функционального конструирования целевого документа XML и встраивания запроса LINQ to XML в некоторый документ XML.

Применение XSLT обладает тем преимуществом, что это — стандартная технология XML. Существует инструментарий, который помогает в написании, отладке и тестировании трансформаций XSLT. Вдобавок, поскольку он уже существует, доступны готовые XSLT-документы, которые можно использовать в новом коде с применением LINQ to XML.

Существует множество доступных XSLT-документов, из которых можно выбрать нужные. К тому же, использование XSLT для трансформаций наиболее динамично. В отличие от применения подхода на основе функционального конструирования LINQ to XML, повторно компилировать код для изменения трансформации не понадобится. Простое изменение документа XSLT позволяет модифицировать трансформации во время выполнения.

И, наконец, XSLT — широко известная технология, и есть немало знающих ее разработчиков, которые могут оказать помощь. Что касается подхода на основе функционального конструирования, то пока, на начальном этапе существования LINQ, это еще не так.

Применение подхода с функциональным конструированием не потребует больших затрат. Оно позволит выполнять трансформации XML, не зная ничего помимо LINQ to XML. Поэтому если вы не знакомы с XSLT, а потребности в трансформации скромны, этот подход может оказаться более подходящим. К тому же, хотя функциональное конструирование менее удобно, чем простая модификация документа XSLT, необходимость перекомпиляции кода для модификации трансформаций может добавить безопасности.

Трансформации с использованием XSLT

Чтобы выполнить трансформацию с использованием XSLT, необходимо обратиться к классам-мостам XmlWriter и XmlReader, экземпляры которых получаются из методов CreateWriter и CreateReader классов XDocument.

Пример, показанный ниже, требует некоторых пояснений, которые будут приводиться по мере продвижения по коду. Для начала определяется таблица стилей трансформации:

using System.Xml.Xsl;
using System.IO;

...

string xsl =
        @"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
          <xsl:template match='//Employees'>
              <html>
                  <body>
                  <h1>Employees</h1>
                  <table>
                      <tr align='left'>
                          <th>Role</th>
                          <th>First Name</th>
                          <th>Last Name</th>
                      </tr>
                      <xsl:apply-templates></xsl:apply-templates>
                  </table>
                  </body>
              </html>
          </xsl:template>
          <xsl:template match='Employee'>
              <tr>
                  <td><xsl:value-of select='@type'/></td>
                  <td><xsl:value-of select='FirstName'/></td>
                  <td><xsl:value-of select='LastName'/></td>
              </tr>
          </xsl:template>
        </xsl:stylesheet>";

Здесь нет ничего особо выдающегося. Просто указывается XSL-разметка для создания некоторой HTML-разметки, предназначенной для отображения типичного XML-документа с сотрудниками в виде HTML-таблицы. Далее создается XML-документ с сотрудниками:

XDocument xDocument = new XDocument(
              new XElement("Employees",
                     new XElement("Employee",
                       new XAttribute("type", "Programmer"),
                       new XElement("FirstName", "Alex"),
                       new XElement("LastName", "Erohin")),
                     new XElement("Employee",
                       new XAttribute("type", "Editor"),
                       new XElement("FirstName", "Elena"),
                       new XElement("LastName", "Volkova"))));

Это просто обычный XML-документ. А теперь о том, где начинается "магия". Необходимо создать новый XDocument для трансформированной версии. Затем из этого документа будет построен XmlWriter, создан экземпляр объекта XslCompiledTransform, загружен объект трансформации с помощью таблицы стилей трансформации и выполнена трансформация входного XML-документа в выходной XmlWriter:

XDocument transformedDoc = new XDocument();
using (XmlWriter writer = transformedDoc.CreateWriter())
{
           XslCompiledTransform transform = new XslCompiledTransform();
           transform.Load(XmlReader.Create(new StringReader(xsl)));
           transform.Transform(xDocument.CreateReader(), writer);
}

Console.WriteLine(transformedDoc);

Разумеется, после всего этого трансформированная версия документа отображается. Как видите, для выполнения трансформации используются оба класса-моста — XmlWriter и XmlReader. Ниже показан результат:

Трансформация документа XML с использованием XSLT

Трансформация с использованием функционального конструирования

Хотя LINQ to XML поддерживает трансформацию XSLT, существуют очень эффективные способы выполнения трансформаций на основе самого API-интерфейса LINQ to XML.

Логически рассуждая, трансформация может быть сведена к комбинации функционально сконструированного дерева XML с встроенным запросом XML. Для выполнения трансформации комбинируйте функциональное конструирование со встроенным запросом XML LINQ.

XML-трансформации лучше всего объяснить на примере. Во многих примерах, которые приводились ранее, посвященных LINQ to XML, работа осуществлялась со следующим деревом XML:

<Employees>
    <Employee type="Programmer">
        <FirstName>Alex</FirstName>
        <LastName>Erohin</LastName>
    </Employee>
    <Employee type="Editor">
        <FirstName>Elena</FirstName>
        <LastName>Volkova</LastName>
    </Employee>
</Employees>

Предположим, что это дерево XML нужно трансформировать в следующее:

<MediaEmployees type="work">
       <Employee Role="Programmer" Name="Alex Erohin" /> 
       <Employee Role="Editor" Name="Elena Volkova" / > 
</MediaEmployees>

Для проведения этой трансформации будет использоваться функциональное конструирование со встроенным запросом. С помощью этого подхода будет функционально сконструирован новый документ, соответствующий желаемой выходной древовидной структуре XML, с получением необходимых для этого данных из исходного документа XML с помощью запроса LINQ to XML. Функциональное конструирование и логика запроса определяются желаемой выходной древовидной структурой. Код представлен ниже:

XDocument xDocument = new XDocument(
              new XElement("Employees",
                     new XElement("Employee",
                       new XAttribute("type", "Programmer"),
                       new XElement("FirstName", "Alex"),
                       new XElement("LastName", "Erohin")),
                     new XElement("Employee",
                       new XAttribute("type", "Editor"),
                       new XElement("FirstName", "Elena"),
                       new XElement("LastName", "Volkova"))));

            Console.WriteLine("Исходный XML-документ: \n");
            Console.WriteLine(xDocument);

            XDocument xTransDocument = new XDocument(
               new XElement("MediaEmployees",
                   new XAttribute("type", "work"),
                   xDocument.Element("Employees").Elements("Employee")
                            .Select(e => new XElement("Employee",
                                new XAttribute("Role", (string)e.Attribute("type")),
                                new XAttribute("Name", (string)e.Element("FirstName") + " " +
                                    (string)e.Element("LastName"))))));
          
            Console.WriteLine("\n\nТрансформированный документ: \n\n" + xTransDocument);

В приведенном коде изначально создается XML-документ xDocument, который будет трансформироваться и отображаться. Затем строится новый трансформированный документ xTransDocument и корневой узел MediaEmployees, включая атрибут type. Затем генерируется элемент Employee для каждого элемента Employee исходного XML с помощью операций Element и Elements. После этого строится сама проекция элемента Employee, используя LINQ-операцию Select И, наконец, трансформированный XML-документ отображается. Давайте посмотрим, получено ли в результате то, что было нужно:

Трансформация документа XML

Все прошло блестяще! Вывод соответствует ожиданиям. Неплохо, если учесть, что не использовалось ничего помимо LINQ to XML.

Пройди тесты
Лучший чат для C# программистов