Стивен Холзнер - XSLT
• *[1][NAME] выбирает любой элемент <NAME>, являющийся первым ребенком своего родителя;
• *[position() < 5] выбирает первые пять детей контекстного узла;
• *[position() < 5][@UNIT] выбирает первые пять детей контекстного узла с атрибутом UNITS;
• text() выбирает все дочерние текстовые узлы контекстного узла;
• text()[starts-with(., "In the course of human events")] выбирает все дочерние текстовые узлы контекстного узла, начинающиеся с "In the course of human events";
• /PLANETS[@UNITS="million miles"] выбирает все элементы PLANETS, у которых значение атрибута UNITS равно "million miles";
• PLANET[/PLANETS/@ [email protected]] выбирает все элементы <PLANET>, у которых значение атрибута REFERENCE такое же, как значение атрибута UNITS элемента PLANETS в корне документа;
• PLANET/* выбирает все дочерние элементы элементов PLANET;
• PLANET/*/DAY выбирает все элементы DAY — правнуки элементов PLANET, являющиеся детьми контекстного узла;
• */* выбирает элементы-внуки текущего элемента;
• astrophysics:PLANET выбирает элемент PLANET в пространстве имен «astrophysics»;
• astrophysics:* выбирает любые элементы в пространстве имен «astrophysics»;
• PLANET[DAY and DENSITY] выбирает все элементы <PLANET>, у которых есть по крайней мере один элемент <DAY> и один элемент <DENSITY>;
• PLANET[(DAY or DENSITY) and MASS] выбирает все элементы <PLANET>, у которых есть по крайней мере один элемент <DAY> или один элемент <DENSITY>, а также по крайней мере один элемент <MASS>;
• PLANET[DAY and not(DISTANCE)] выбирает все элементы <PLANET>, у которых есть по крайней мере один элемент <DAY> и нет элементов <DISTANCE>;
• PLANET[MASS=/STANDARD/REFERENCE/MASS] выбирает все элементы <PLANET>, у которых значение элемента <MASS> равно значению элемента /<STANDARD>/<REFERENCE>/<MASS>.
На этом мы завершаем в данный момент рассмотрение образцов выбора; связанный материал приводится в главе 7 при рассмотрении выражений XPath. Глава 5 начинается с изучения способов работы с данными в XML-документах путем сортировки и принятия решения на основе значений данных.
Глава 5
Принятие решений и сортировка данных
Эта глава посвящена принятию решений, сортировке и различным способам обработки данных в ХМL-документах. Мы рассмотрим элементы <xsl:if>, <xsl:choose>, <xsl:when>, <xsl:otherwise>, <xsl:for-each> и <xsl:sort>, при помощи которых вы сможете обрабатывать данные и управлять выполнением преобразования в зависимости от значения данных.
Однако эти элементы не предоставляют такой точности, как в языках программирования. Поэтому я также представлю в этой главе расширения XSLT, в том числе элемент рабочего проекта XSLT 1.1 <xsl:script>. Этот элемент был предназначен для упрощения применения Java и JavaScript с процессором XSLT. (Для чтения этой книги нет необходимости владеть Java или JavaScript, но если вы знаете эти языки, вам будет приятно удостовериться, что некоторые процессоры XSLT дают возможность использовать их при преобразованиях XML.) Нечто похожее на этот элемент обязательно появится в XSLT 2.0. При помощи расширений вы можете расширять спецификацию XSLT, добавляя в XSLT новые элементы и функции какого-либо производителя или свои собственные.
Кроме того, в этой главе мы также рассмотрим, как перенумеровать элементы в документе, что делать в случае, когда ваш процессор XSLT не поддерживает определенное расширение, и многое другое. Я начну с наиболее часто используемого элемента из рассматриваемых в данной главе: <xsl:if>.
Элемент <xsl:if>
При помощи элемента <xsl:if> осуществляются проверки условия и принимаются действия на основе результата проверки. Он во многом похож на оператор if в языках программирования. У элемента <xsl:if> один атрибут:
• test (обязательный). Устанавливается в значение логического (Boolean, true/false) условия, которое вы хотите проверить.
Элемент заключает в себе тело шаблона.
Вот как это работает: вы включаете тело шаблона внутрь элемента <xsl:if>, проверяющего какое-то выражение. Если это выражение истинно, тело шаблона используется, если ложно — игнорируется:
<xsl:if test="expression">
<!--template body-->
</xsl:if>
Можно проверять любое выражение XPath. Для преобразования его в значения true/false в элементе <xsl:if> применяйте следующие правила:
• если выражение вычисляется в набор узлов, оно трактуется как true, когда набор узлов содержит хотя бы один узел;
• выражение-строка считается true, если строка не пуста;
• фрагмент результирующего дерева трактуется как true, если содержит узлы;
• если результат выражения — число, он считается true, когда отличен от нуля.
Элемент <xsl:if> во многом похож на оператор if-then в языках программирования. Однако не существует оператора <xsl:else> для формирования конструкций if-then-else — для этого служит элемент <xsl:choose>.
В листинге 5.1 я перечисляю планеты в planets.xml одну за другой и добавляю горизонтальное правило HTML, элемент <HR> (horizontal rule), после последнего элемента — но только после последнего. При помощи <xsl:if> это можно сделать так.
Листинг 5.1. Применение <xsl:if><?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="PLANETS">
<HTML>
<HEAD>
<TITLE>
Planets
</TITLE>
</HEAD>
<BODY>
<xsl:apply-templates select="PLANET"/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="PLANET">
<P>
<xsl:value-of select="NAME"/>
is planet number <xsl:value-of select="position()"/> from the sun.
</P>
<xsl:if test="position() = last()"><HR/><xsl:if>
</xsl:template>
</xsl:stylesheet>
Вот результат — как видите, элемент <HR> появляется только после последней перечисленной планеты:
<HTML>
<HEAD>
<TITLE>
Planets
</TITLE>
</HEAD>
<BODY>
<P>
Mercury is planet number 1 from the sun.
</P>
<P>
Venus is planet number 2 from the sun.
</P>
<P>
Earth is planet number 3 from the sun.
</P>
<HR>
</BODY>
</HTML>
Рассмотрим еще один пример — преобразование XML-XML, в котором перечисляются планеты из planets.xml. Однако я хочу, чтобы выводилось не просто «The first three planets are: Mercury Venus Earth» (первые три планеты: Меркурий Венера Земля), a «The first three planets are: Mercury, Venus, and Earth». Необходимые знаки пунктуации можно добавить, определяя текущий элемент при помощи функции position и проверяя позицию при помощи <xsl:if> (листинг 5.2).
Листинг 5.2. Второй пример применения <xsl:if><?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="PLANETS">
<DOCUMENT>
<TITLE>
The Planets
</TITLE>
<PLANETS>
The first three planets are: <xsl:apply-templates select="PLANET"/>
</PLANETS>
</DOCUMENT>
</xsl:template>
<xsl:template match="PLANET">
<xsl:value-of select="NAME"/>
<xsl:if test="position()!=last()">, </xsl:if>
<xsl:if test="position()=last()-1">and </xsl:if>
<xsl:if test=position()=last()">.</xsl:if>
</xsl:template>
</xsl:stylesheet>
Вот результат:
<?xml version=" 1.0" encoding="UTF-8"?>
<DOCUMENT>
<TITLE>
The Planets
</TITLE>
<PLANETS>
The first three planets are: Mercury, Venus, and Earth
</PLANETS>
</DOCUMENT>
Как видите, я смог добавить правильные знаки пунктуации, определяя место в документе при помощи <xsl:if>.
При помощи <xsl:if> можно также обнаруживать ошибки во время преобразования. Например, при помощи <xsl:if> можно вывести сообщение, есть ли в planets.xml элемент <NAME> (листинг 5.3).
Листинг 5.3. Обнаружение ошибок при помощи <xsl:if><?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>