[an error occurred while processing this directive] IT • archiv :: Print

IT • archiv


[an error occurred while processing this directive]

[an error occurred while processing this directive]

Обработка документов XML с использованием XPath и XSLT

[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive](none)
[an error occurred while processing this directive]([an error occurred while processing this directive] Андре Тост [an error occurred while processing this directive])

[an error occurred while processing this directive](none)

В статье рассматриваются способы снижения сложности кода на Java при обработке документов XML за счет использования XPath и XSLT.

XML Tutorial
PDF versionPDF версия
Обзор
Для обработки документа XML приложение на Java обычно использует API объектной модели документа (DOM), для которой существует стандарт W3C. В этой статье Andrй Tost рассматривает возможности стандартов XSLT и XPath для решения определенных задач обработки и показывает, что эти решения могут быть более элегантными и эффективными, чем обычное использование DOM API. В действительности, совместное использование DOM, XSLT, и XPath при программировании различных задач, позволит создать наилучший код. Tost анализирует два примера — один с использованием XPath и другой с использованием таблицы стилей XSLT — с помощью которых демонстрирует способы применения этих технологий в Java. (1500 слов)

Расширяемый язык разметки(XML) несомненно представляет из себя одну из самых ярких технологий современности. Хотя концепция языков разметки отнюдь не нова, XML оказался особенно привлекательным для программистов на Java и для Internet. Программный интерфейс Java для разбора XML (JAXP; см. Ресурсы), определенный недавно сообществом Java Community Process, по-видимому приведет к созданию универсального интерфейса доступа к документам XML. W3C описал так называемую объектную модель документа (DOM), которая служит в качестве стандартного интерфейса для работы с документами XML в древовидной иерархии, тогда как простой программный интерфейс для XML (SAX) предусматривает программный способ последовательной обработки документа XML, основанный на модели обработки событий. Оба стандарта (причем SAX является стандартом de facto) дополняют JAXP. Вместе эти три стандартных API обеспечивают эффективную поддержку для обработки документов XML на Java, и их использование описано в многочисленных книгах.

В этой статье рассматриваются методы обработки документов XML, которые выходят за рамки стандартного API Java, используемого для этих целей. Мы увидим, что во многих случаях XPath и XSLT предоставляют более простой, более изящный путь для решения задач, возлагаемых на приложение. На простых примерах мы сравним решение на базе чистого кода Java для XML с решением, использующим XPath и/или XSLT.

XSLT и XPath входят в состав спецификации расширяемого языка стилей (XSL) (см. Ресурсы). XSL состоит их трех частей: спецификаций самого языка XSL, XSL-фильтров (XSLT) и языка XML Path (XPath). XSL — язык для описания преобразований документов XML; он содержит определения, согласно которым объекты данных документов XML переформатируются в требуемое наглядное представление. XSLT определяет набор команд для преобразований одного документа XML в другой. Можно считать, что XSLT представляет собой XSL за вычетом объектов данных. Язык XPath применяется в специальных разделах документов XML и предназначен для использования в таблицах стилей XSLT.

В дальнейшем в этой статье предполагается, что читатель знаком с основами XML и XSLT, а также с API DOM. (За информацией по этим вопросам предлагаем ознакомиться со списком ресурсов в конце статьи.)

Замечание
Примеры кодов программ, приводимые в настоящей статье были откомпилированы и отлажены с использованием XML-парзера Xerces фирмы Apache и процессора XSL Xalan той же фирмы (см. Ресурсы).

Постановка задачи

Во многих статьях и документах на тему XML говорится, что этот язык хорошо подходит для практического подхода к программированию в Сети: при этом ключевым моментом является шаблон модель-представление-управление (MVC), или, проще говоря, отделение данных приложений от данных представления. Если данные приложения отформатировать в XML, их можно затем легко связать с шаблоном HTML или таблицей стилей XSL, что делается обычно с помощью сервлетов или по технологии Java ServerPage.

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

Поскольку данные, отформатированные в XML являются независимыми от языка, возможно использовать их в тех случаях, когда клиент некоторого приложения точно не определен или когда требуется обеспечить независимость его от сервера. Например, в среде B2B зависимость двух сторон, участвующих в обмене данными, от конкретного объектного интерфейса Java может оказаться неприемлемым. Такие требования выдвигаются новыми технологиями, такими как протокол простого доступа к объектам (SOAP) (см. Ресурсы).

Все перечисленные случаи имеют одну общую черту: данные хранятся в документах XML и должны быть обработаны каким-либо приложением. Например, в приложении, которое использует различные компоненты от различных поставщиков, вероятнее всего придется изменить структуру данных (XML) для того, чтобы она удовлетворяла требованиям приложения или соответствовала определенному стандарту.

Вышеупомянутый код на Java, определенно удовлетворяет всем этим требованиям. Более того, появляется все большее количество средств, с помощью которых пользователь может превратить документ XML в JavaBean и обратно, обеспечивая при этом простые методы обработки данных в программе на Java. Во многих случаях, однако, приложение или какая-либо из его функций всего лишь обрабатывает один или несколько документов XML, поступающих в качестве входной информации и преобразует их в другие документы XML на выходе. В этих случаях использование стилей является хорошей альтернативой, как мы покажем далее в этой статье.

Использование XPath для поиска элементов документа XML

Как говорилось выше, язык XPath служит для поиска определенных частей документа XML. При этом подразумевается, что он используется фильтром XSLT, однако ничто не удерживает нас от использования его в программе на Java во избежание длительных итераций по иерархии элементов DOM. В действительности, мы можем заставить работать на нас процессор XSLT/XPath. Рассмотрим как можно реализовать такую работу.

Предположим, что сценарий нашего приложения предполагает представление исходного текста документа XML пользователю (возможно после преобразования с использованием фильтра). Пользователь вносит изменения в данные и для снижения нагрузки на сеть, отсылает обратно в приложение только измененные записи. Приложение ищет в исходном документе фрагмент XML, в котором сделаны изменения и заменяет его данные на новые.

Рассмотрим небольшой пример кода, который поможет нам разобраться с имеющимися возможностями. В описанном примере, мы предполагаем, что приложение работает с записями, которые содержат информацию об адресе. Пример документа addressbook выглядит следующим образом:

<addressbook>
    <address>
       <addressee>John Smith</addressee>
       <streetaddress>250 18th Ave SE</streetaddress>
       <city>Rochester</city>
       <state>MN</state>
       <postalCode>55902</postalCode>
    </address>
    <address>
       <addressee>Bill Morris</addressee>
       <streetaddress>1234 Center Lane NW</streetaddress>
       <city>St. Paul</city>
       <state>MN</state>
       <postalCode>55123</postalCode>
 </address>
 </addressbook>

Приложение (возможно, хотя и не обязательно, сервлет) сохраняет экземпляр элемента addressbook в памяти как объект Document модели DOM. Когда пользователь изменяет адрес, через внешний интерфейс приложению сообщается только модифицированный элемент <address>.

Элемент <addressee> служит как первичный ключ и является уникальным идентификатором адреса. Для реального приложения в этом нет большого смысла, но мы делаем так, чтобы пример выглядел проще.

Теперь мы должны написать некоторый код на Java, который поможет нам определить тот элемент <address> в исходном дереве, содержимое которого необходимо заменить на информацию в измененном элементе. Проиллюстрируем сказанное с помощью реализации метода findAddress(), код которого представлен ниже. Отметим, что для уменьшения размера кода в примере обработка ошибок была опущена.

public Node findAddress(String name, Document source) {
    Element root = source.getDocumentElement();
    NodeList nl = root.getChildNodes();

    // итерация по всем элементам address
    // и поиск того элемента, который содержит нужного адресата
    for (int i=0;i<nl.getLength(); i++) {
       Node n = nl.item(i);
       if ((n.getNodeType() == Node.ELEMENT_NODE) &&
           (((Element)n).getTagName().equals("address"))) {
          // имеем узел address, теперь надо найти
          // подчиненнй узел 'addressee'
          Node addressee = ((Element)n).
 	getElementsByTagName("addressee").item(0);

          // имеем адресата, выбираем текстовый элемент для сравнения
          Node child = addressee.getChildNodes().item(0);
          do {
             if ((child.getNodeType()==Node.TEXT_NODE) &&
                 (((Text)child).getData().equals(name))) {
                return n;
             }
             child = child.getNextSibling();
                   } while (child != null);
       }
    }
    return null;
 }

Приведенный выше код скорее всего может быть оптимизирован, но очевидно, что выполнение итераций по дереву DOM может быть утомительным и приводить к ошибкам. Рассмотрим теперь возможности использования простой инструкции XPath для нахождения целевой узла. Инструкция может выглядеть следующим образом:

//address[child::addressee[text() = 'Jim Smith']]

Теперь можно переписать предыдущий метод. На этот раз мы используем выражение XPath для поиск нужного нам узла:

public Node findAddress(String name, Document source)
						throws Exception {
    // надо создать несколько вспомогательных объектов
    XMLParserLiaison xpathSupport = new XMLParserLiaisonDefault();
    XPathProcessor xpathParser = new XPathProcessorImpl(xpathSupport);
    PrefixResolver prefixResolver =
 	new PrefixResolverDefault(source.getDocumentElement());

    // создать и инициализировать объект XPath
    XPath xp = new XPath();
    String xpString = "//address[child::addressee[text() =
 	'"+name+"']]";
    xpathParser.initXPath(xp, xpString, prefixResolver);

    // выполнить выборку с использованием XPath
    XObject list =
 	xp.execute(xpathSupport,
 		source.getDocumentElement(), prefixResolver);

    // возвратить результат
    return list.nodeset().item(0);
 }

Приведенный здесь код возможно выглядит не намного лучше, чем в предыдущем примере, но большая часть содержимого этого метода может быть спрятана во вспомогательном классе. Единственная часть, которая будет затем постоянно изменяться — выражение XPATH и целевой узел.

Таким образом мы можем создать класс XPathHelper, который будет выглядеть следующим образом:

import org.w3c.dom.*;
 import org.xml.sax.*;

 import org.apache.xalan.xpath.*;
 import org.apache.xalan.xpath.xml.*;

 public class XPathHelper {

    XMLParserLiaison xpathSupport = null;
    XPathProcessor xpathParser = null;
    PrefixResolver prefixResolver = null;

    XPathHelper() {
       xpathSupport = new XMLParserLiaisonDefault();
       xpathParser = new XPathProcessorImpl(xpathSupport);
    }

    public NodeList processXPath(String xpath, Node target)
 					throws SAXException {
       prefixResolver = new PrefixResolverDefault(target);

       // создать и инициализировать XPath
       XPath xp = new XPath();
       xpathParser.initXPath(xp, xpath, prefixResolver);

       // выполнить выборку с помощью XPath
       XObject list = xp.execute(xpathSupport, target, prefixResolver);

       // вернуть результирующий узел
       return list.nodeset();
    }
 }

После создания вспомогательного класса, мы можем вновь переписать наш основной метод, который станет теперь совсем коротким:

public Node findAddress(String name, Document source) throws Exception {

    XPathHelper xpathHelper = new XPathHelper();
    NodeList nl = xpathHelper.processXPath(
         "//address[child::addressee[text() =
 		'"+name+"']]",
         source.getDocumentElement());
    return nl.item(0);
 }

Вспомогательный класс можно теперь использовать всякий раз, когда потребуется найти узел или группу узлов в данном документе XML. Фактическая инструкция XPath может быть даже загружена из внешнего источника, так, чтобы изменения можно было делать на лету, если изменяется структура исходного документа. В этом случае даже не потребуется компиляция.

Обработка документа XML с помощью фильтров XSL

В отдельных случаях имеет смысл всю обработку документа XML полностью вынести во внешний фильтр XSL, в некотором отношении этот процесс похож на использование XPath, как обсуждалось в предыдущем разделе. С помощью фильтров XSL можно создавать выходные документы выбором узлов из входного документа и объединением их содержимого с содержимым фильтра по правилам, заданным в последнем.

Если приложение изменяет структуру и содержание документа XML и создает новый документ, использование таблиц стилей может оказаться лучшим и более легким способом обработки чем выполнение той же работы в коде программы на Java. Чаще всего таблицы стилей (фильтры) хранятся во внешних файлах, позволяя пользователю изменить его на лету, без потребности перекомпилировать программу.

Например, можно выполнить обработку примера addressbook простым созданием таблицы стилей, которая объединяет имеющуюся версию addressbook с обновленной, создавая при этом новый документ с отражением в нем всех изменений.

Приведем пример такого фильтра:

<?xml version='1.0'?>

 <xsl:stylesheet xmlns:xsl=
 	"http://www.w3.org/1999/XSL/Transform"
 		version="1.0">
    <xsl:output method="xml"/>

 <xsl:variable name=
 "doc-file">http://mymachine.com/changed.xml</xsl:variable>

 <!-- копирует все, что не имеет другого шаблона -->
 <xsl:template match="* | @*">
    <xsl:copy><xsl:copy-of select=
 	"@*"/><xsl:apply-templates/></xsl:copy>
 </xsl:template>

 <!-- проверка каждого элемента <address> на существование обновления -->
 <xsl:template match="//address">
    <xsl:param name="addresseeName">
       <xsl:value-of select="addressee"/>
    </xsl:param>

    <xsl:choose>
       <xsl:when test=
 	"document($doc-file)//addressee[text()=$addresseeName]">
          <xsl:copy-of select=
 	"document($doc-file)//address[child::addressee[text()=
 		$addresseeName]]"/>
       </xsl:when>
       <xsl:otherwise>
          <xsl:apply-templates/>
       </xsl:otherwise>
    </xsl:choose>
 </xsl:template>

 </xsl:stylesheet>

Заметим, что вышеупомянутая таблица стилей берет модифицированные данные из файла с именем changed.xml. Очевидно, что в реальном приложении не хотелось бы сохранять измененные данные в файле перед их обработкой. Решение этой проблемы состоит в том, чтобы добавить специальный атрибут к элементу <address>, который указывает, что элемент был модифицирован. Тогда приложение могло бы просто добавлять модифицированные данные к исходному документу и определять использовать отдельную таблицу стилей, которая бы обнаруживала модифицированные записи и заменяла ими старые.

Теперь приложение должно создать объект XSLTProcessor и предоставить ему всю работу:

import org.apache.xalan.xslt.*;

    ...
    XSLTProcessor processor = XSLTProcessorFactory.getProcessor();
    processor.process(new XSLTInputSource(sourceDoc.getDocumentElement(),
                      new XSLTInputsource(
 	"http://mymachine.com/updateAddress.xsl"),
                      new XSLTResultTarget(newDoc.getDocumentElement());
    sourceDoc = newDoc;
    ...

Заключение

Для большинства программистов на Java, XML является относительно новой технологией, которую предстоит освоить. В этой статье показано, что ручные разбор и обработка документов XML является одним из вариантов, что можно использовать выражения XPath и фильтры XSL, чтобы избавиться от большого количества обработки в коде и итераций, снижая таким образом размер кода. Более того, в такой системе информация о том, как обрабатывать данные сохраняется во внешних файлах, поэтому может изменяться без перекомпиляции исходных текстов приложения. Описанный здесь механизм можно использовать для создания данных презентаций для приложений Web, но также может использоваться во всех случаях, где необходимо обрабатывать данные XML.

Об авторе

Андре Тост работает консультирующим инженером-программистом в группе разработчиков IBM's в Рочестере, Миннесота. Он закончил профессиональную академию Штутгарт, Германия и работал над различными проектами объектно-ориентированной разработки в течение последних шести лет. В настоящее время он работает в технической архитектурной группе IBM WebSphere Business Components, которая специализируется на механизмах использования XML и XSL.

Ресурсы

Recent XML articles in JavaWorld

XML help

Other valuable XML-related resources

Reprinted with permission from the September 2000 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://javaworld.com/ javaworld/jw-09-2000/jw-0908-xpath.html

[an error occurred while processing this directive]
[an error occurred while processing this directive] Перевод на русский © Андрей Ковалев, к.т.н., доцент МИЭТ, 2000
< Вернуться на caйт :: Copyright © 1999 — 2010, IT • archiv.