XSLT - Стивен Холзнер
Шрифт:
Интервал:
Закладка:
<xsl:template match="PLANETS//NAME">
<H3>
<xsl:value-of select="."/>
</H3>
</xsl:template>
Выбор атрибутов
Как было показано в главе 3, «Создание и применение шаблонов», можно выбирать атрибуты, если предварять их имена префиксом @. Вы уже работали с атрибутом UNITS, который поддерживают большинство детей элементов <PLANET>:
<PLANET>
<NAME>Earth</NAME>
<MASS UNITS="(Earth = 1)">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>
Чтобы извлечь единицы измерения и отобразить их вместе со значениями массы и т.п., можно выбрать атрибут UNITS при помощи @UNITS (листинг 4.1).
Листинг 4.1. Выбор атрибутов<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/PLANETS">
<HTML>
<HEAD>
.
.
.
</HEAD>
<BODY>
.
.
.
</BODY>
</HTML>
</xsl:template>
<xsl:template match="PLANET">
<TR>
<TD><xsl:value-of select="NAME"/></TD>
<TD><xsl:apply-templates select="MASS"/></TD>
<TD><xsl:apply-templates select="RADIUS"/></ТD>
</TR>
</xsl:template>
<xsl:template match="MASS">
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
<xsl:value-of select="@UNITS"/>
</xsl:template>
<xsl:template match="RADIUS">
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
<xsl:value-of select="@UNITS"/>
</xsl:template>
<xsl:template match="DAY">
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
<xsl:value-of select="@UNITS"/>
</xsl:template>
</xsl:stylesheet>
Теперь результирующая HTML-таблица включает не только значения, но и их единицы измерения:
<HTML>
<HEAD>
<TITLE>
The Planets Table
</TITLE>
</HEAD>
<BODY>
<H1>
The Planets Table
</H1>
<TABLE>
<TR>
.
.
.
<TR>
<TD>Mercury</TD>
<TD>.0553 (Earth = 1)</TD>
<TD>1516 miles</TD>
</TR>
<TR>
<TD>Venus</TD>
<TD>.815 (Earth = 1)</TD>
<TD>3716 miles</TD>
</TR>
.
.
.
</TABLE>
</BODY>
</HTML>
Для выбора всех атрибутов элемента можно также использовать подстановку. Например, "PLANET/@*" выбирает все атрибуты элементов <PLANET>.
Формальное определение образцов выбора
Определение образцов выбора приводится также в рекомендации XSLT W3C. Образцы выбора определяются в терминах выражений XPath следующим образом: «Синтаксис для образцов является подмножеством для выражений [XPath]. В частности, пути расположения, удовлетворяющие определенным ограничениям, могут использоваться как образцы. Выражение, в то же время являющееся образцом, всегда вычисляется в объект типа набора узлов. Узел удовлетворяет образцу, если узел является членом результата вычисления образца как выражения по отношению к возможному контексту; возможный контекст — это контекст, контекстный узел которого был выбран, или один из его предков».
Самое важное предложение в предыдущем абзаце — последнее. Суть в том, что узел X удовлетворяет образцу тогда и только тогда, когда существует узел X или предок X, такой, что при применении к этому узлу образца как выражения XPath, результирующий набор узлов будет включать X.
Что в действительности это означает? Это значит, что когда нужно проверить, удовлетворяет ли узел образцу, сначала следует применить образец как выражение XPath к самому узлу, затем применить его последовательно ко всем его предкам, вплоть до корневого узла. Если какой-либо полученный при этом набор узлов будет содержать сам узел, узел удовлетворяет образцу. Такой порядок действий имеет смысл потому, что образцы выбора пишутся для применения к текущему узлу или его дочерним узлам.
СЛЕДСТВИЯ ФОРМАЛЬНОГО ОПРЕДЕЛЕНИЯ ОБРАЗЦОВ ВЫБОРА
Приведенное определение образцов в терминах выражений XPath довольно очевидно, но существуют следствия, которые сразу не видны. Например, хотя функция node() определена как функция, выбирающая любой узел, при использовании ее в качестве образца, "node()", в действительности она представляется как "child::node()", как вы увидите позже в этой главе. Помимо прочего, это означает, что образец "node()" может выбирать только дочерние узлы — он никогда не выберет корневой узел. Отметьте также, что нет образцов, которые бы могли выбрать узлы объявлений пространств имен.
W3C дает формальное определение образцов выбора в нотации расширенных форм Бэкуса-Наура (РБНФ), при помощи которой написана и спецификация XML. Объяснение этой грамматики можно найти по адресу www.w3.org/TR/REC-xml (раздел 6). Здесь я привожу формальное определение образцов только для справки. (Разъяснению этого формального определения посвящена целая глава.) В следующем списке приведены используемые здесь лексемы нотации РБНФ:
• ::= означает «определяется как»;
• + означает «один или больше»;
• * означает «ноль или больше»;
• | означает «или»;
• - означает «не»;
• ? означает «необязательно».
Далее приведено настоящее, формальное определение образцов выбора W3C; когда элемент заключен в одиночные кавычки, как 'child' или '::', это значит, что элемент должен появиться в образце буквально (как "child::NAME"), — такие элементы называются литералами, Literal:
Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
LocationPathPattern ::= '/' RelativePathPattern?
| IdKeyPattern ('/' | '//') RelativePathPattern?
| '//'? RelativePathPattern
IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal '.' Literal ')'
RelativePathPattern ::= StepPattern | RelativePathPattern '/' StepPattern
| RelativePathPattern '//' StepPattern
StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
| ('child' | 'attribute') '::'
Определения NodeText (текстового узла) и Predicate (предиката) приводятся в спецификации XPath (Expr соответствует выражению XPath, a NCName и QName были определены в начале главы 2, «Создание и применение таблиц стилей»):
NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'
Predicate ::= '[' PredicateExpr ']'
PredicateExpr ::= Expr
AbbreviatedAxisSpecifier ::= '@'?
NameTest :: = '*' | NCName ':' '*' | QName
NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
Как вы можете видеть, все это больше походит на какой-то код. Давайте начнем его расшифровывать. Во-первых, образец (pattern) состоит из одного (или более) образца пути расположения (location path pattern). Образец пути расположения, в свою очередь, состоит из одного или нескольких образцов шага (step pattern), разделенных / или //, или одним (несколькими) образцом шага в объединении с функциями id и key (выбирающими элементы с определенными идентификаторами или ключами).
Образцы шага являются строительными блоками шаблонов: в одном пути можно использовать несколько шагов, разделяя их символами / или //, как в образце "PLANET/*/ NAME", в котором три шага: "PLANET", "*" и "NAME". Если вы начнете сам образец с символа /, он будет называться абсолютным, так как вы указали образец от корневого узла (как в "/PLANETS/PLANET" или "//PLANET"); иначе образец называется относительным и применяется начиная с контекстного узла (как в "PLANET").
Затем образец шага состоит из оси, условия узла и предикатов (которых может и не быть). Например, в выражении child::PLANET[position()=5], child — это имя оси, PLANET — условие узла, a [position()=5] — это предикат. (Предикаты всегда заключены в квадратные скобки.) Образцы можно создавать при помощи одного или более образцов шага, как, например, образец /child::PLANET/child::NAME, который выбирает элементы <NAME>, дочерние по отношению к родителю <PLANET>.
Таким образом, чтобы понять работу образцов, вам необходимо понять работу образцов шага, поскольку образцы состоят из одного или более образцов шага, в таких выражениях, как "step-pattern1/step-pattern2/step-pattern3…". А чтобы понять работу образца шага, необходимо понять работу деятельности трех составных частей — осей, условий узлов и предикатов, которыми мы и займемся в следующих разделах.