Стивен Холзнер - XSLT
Предикаты являются полными выражениями XPath, хотя на предикаты, используемые в образцах, накладывается два ограничения:
• когда образец используется в атрибуте match, предикат не должен содержать никаких ссылок на переменные XSL (которые обсуждаются в главе 9). Это ограничение не применяется к предикатам, используемым в элементах <xsl:number>;
• образцы не могут использовать в предикатах функцию XPath current, возвращающую текущий узел. Ее применение ограничено, поэтому обработка зависит от реализации и не зависит от текущего состояния обработки.
В следующем примере образец выбирает элементы <PLANET> с дочерними элементами <NAME>:
<xsl:template match="PLANET[NAME]">
.
.
.
</xsl:template>
Этот образец выбирает любой элемент с дочерним элементом <NAME>:
<xsl:template match="*[NAME]">
.
.
.
</xsl:template>
Теперь я задал элементам <PLANET> в planets.xml новый атрибут, COLOR, устанавливающий цвет планеты:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="planets.xsl"?>
<PLANETS>
<PLANET COLOR="RED">
<NAME>Mercury</NAME>
<MASS UNITS="(Earth = 1)">.0553</MASS>
<DAY UNITS="days">58.65</DAY>
<RADIUS UNITS="miles">1516</RADIUS>
<DENSITY UNITS="(Earth = 1)">.983</DENSITY>
<DISTANCE UNITS="million miles">43.4</DISTANCE><!--В перигелии-->
</PLANET>
<PLANET COLOR="WHITE">
<NAME>Venus</NAME>
<MASS UNITS="(Earth = 1)">.815</MASS>
<DAY UNITS="days">116.75</DAY>
<RADIUS UNITS="miles">3716</RADIUS>
<DENSITY UNITS="(Earth = 1)">.943</DENSITY>
<DISTANCE UNITS="million miles">66.8</DISTANCE><!--B перигелии-->
</PLANET>
<PLANET COLOR="BLUE">
<NAME>Earth</NAME>
<MASS UNITS="(Earth = 1)></MASS>
<DAY UNITS="days">1</DAY>
<RADIUS UNITS="miles">2107</RADIUS>
<DENSITY UNITS="(Earth = 1)">1</DENSITY>
<DISTANCE UNITS="million miles">128.4</DISTANCE><!--B перигелии-->
</PLANET>
</PLANETS>
Следующее выражение выбирает элементы <PLANET> с атрибутом COLOR:
<xsl:template match="PLANET[@COLOR]">
.
.
.
</xsl:template>
А что, если нам требуется выбрать планеты, у которых атрибут COLOR имеет значение "BLUE" (голубой)? Это можно сделать при помощи операции =, как показано в листинге 4.5.
Листинг 4.5. Применение операции =<"xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="PLANETS">
<HTML>
<xsl:apply-templates/>
</HTML>
</xsl:template>
<xsl:template match="PLANET[@COLOR = 'BLUE']">
The <xsl:value-of select="NAME"/> is blue.
</xsl:template>
<xsl:template match="text()">
</xsl:template>
</xsl:stylesheet>
Таблица стилей из листинга 4.5 отбирает планеты с голубым цветом и убирает остальные, выключая правило по умолчанию для текстовых узлов. Результат следующий:
<HTML>
The Earth is blue.
</HTML>
Создание предикатов
Предикаты — настоящие выражения XPath, и XPath гораздо ближе к настоящему языку, чем образцы: к примеру, выражения XPath могут возвращать не только списки узлов, но также логические, строковые и числовые значения. Выражения XPath могут работать не только с текущим узлом или дочерними узлами: можно работать с родительскими узлами, узлами-предками и другими узлами.
Глава 7 полностью посвящена XPath, но имеет смысл предоставить введение в предмет здесь, при обсуждении образцов, потому что часть предиката образца обладает наибольшими возможностями. В предикатах могут быть все виды выражений; в следующем списке перечислен ряд возможных типов, которые будут изучены в следующих разделах:
• наборы узлов;
• логические выражения;
• числа;
• строки.
Предикаты: наборы узлов
Набор узлов (node set), как понятно из названия, представляет собой просто совокупность узлов (и может содержать только один узел). Выражение child::PLANET возвращает набор узлов, состоящий из всех элементов <PLANET>. Выражение child::PLANET/child::NAME возвращает список узлов, состоящий из всех элементов <NAME>, дочерних по отношению к элементам <PLANET>. Для выбора узла или узлов из набора узлов воспользуйтесь следующими функциями для работы с наборами узлов в предикатах:
• last(). Возвращает количество узлов в наборе узлов;
• position(). Возвращает позицию контекстного узла в контекстном наборе узлов (начиная с 1);
• count(node-set). Возвращает количество узлов в наборе. Если опустить node-set, функция будет применена к контекстному узлу;
• id(string ID). Возвращает набор узлов, содержащий элемент с ID, удовлетворяющим переданной функции строке, или пустой набор узлов, если такой элемент отсутствует. Можно перечислить несколько идентификаторов, разделенных символами-разделителями, — тогда функция вернет набор узлов, состоящий из элементов с этими идентификаторами;
• local-name(node-set). Возвращает локальное имя первого узла в наборе узлов. Если опустить node-set, функция будет применена к контекстному узлу;
• namespace-uri(node-set). Возвращает URI пространства имен первого узла в наборе узлов. Если опустить node-set, функция будет применена к контекстному узлу;
• name(node-set). Возвращает полностью определенное имя первого узла в наборе узлов. Если опустить node-set, функция будет применена к контекстному узлу.
В листинге 4.6 я перенумеровал элементы в выходном документе при помощи функции position().
Листинг 4.6. Применение функции position<?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>
The Planets
</TITLE>
</HEAD>
<BODY>
<xsl:apply-templates select="PLANET"/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="PLANET">
<P>
<xsl:value-of select="position()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="NAME/>
</P>
</xsl:template>
</xsl:stylesheet>
Вот результат. Как видите, планеты перенумерованы:
<HTML>
<HEAD>
<TITLE>
The Planets
</TITLE>
</HEAD>
<BODY>
<P>
1. Mercury
</P>
<P>
2. Venus
</P>
<P>
3. Earth
</P>
</BODY>
</HTML>
Можно также применять функции для работы с наборами узлов в предикатах, как, например, PLANET[position()=last()], выбирающая последнего ребенка <PLANET> контекстного узла
Предикаты: логические значения
В выражениях XPath можно также использовать логические (Boolean) значения. Для чисел ноль принимается за ложь (false), другие значения — за истину (true). Пустая строка, "", также считается ложью, все остальные строки — истиной.
Для вычисления логических результатов true/false можно применять следующие логические операции XPath:
• != означает «не равно»;
• < означает «меньше, чем» (в документах XML или XSL используйте <);
• <= означает «меньше или равно» (в документах XML или XSL используйте <=);
• = означает «равно» (программисты на С, С++, Java и JavaScript, обратите внимание: эта операция пишется как один знак =, а не два);
• > означает «больше, чем»;
• >= означает «больше или равно».
ИСПОЛЬЗОВАНИЕ СИМВОЛА <
Особенно обратите внимание на то, что непосредственно в документах XML или XSL нельзя использовать символ <, необходимо использовать ссылку на сущность <.
Для связи логических выражений логическими операциями And и Or используются ключевые слова and и or; слово not инвертирует логический смысл выражения — с истины на ложь или со лжи на истину.
В листинге 4.7 я определяю элемент <PLANET> Земли и помещаю в таблицу строки "Earth", "needs", "no" и "introduction" вместо числовых данных Земли. Я определяю, которая из планет есть Земля, при помощи предиката "[NAME='Earth']", проверяющего значение элемента <NAME>, которое, в свою очередь, представляет собой заключенный в элементе текст. Я также предоставил шаблон для других планет, удовлетворяющих предикату "[NAME!='Earth']''.