До сих пор материал книги был достаточно очевиден, за исключением одного: несколько загадочных образцов выбора (match pattern). Мы работали с различными образцами выбора, такими как «/PLANETS» в элементах
, не предлагая систематического объяснения того, как в действительности работают эти образцы, — как в этом случае:
The Planets Table
.
.
.
В этой главе мы рассмотрим все необходимое, что нужно знать для создания образцов выбора в XSLT. Образцы выбора применяются в элементах
,
и
; с элементом
мы работали с самого начала книги, элемент
мы увидим в главе 9 и элемент
— в главе 5. В частности, для выбора по образцу у элементов
и
используется атрибут match
, а у элемента
— атрибуты count
и from
.
Образцы выбора можно также применять в атрибуте
select
таких элементов, как
,
,
и
. Здесь важно отметить один момент: атрибут select
этих элементов обладает большими возможностями, чем атрибуты match
, count
и from
элементов
,
и
, потому что в select
можно использовать полные выражения XPath, а не только образцы выбора.
Образцы выбора являются подмножеством выражений XPath, то есть все образцы выбора являются допустимыми выражениями XPath, но не все выражения XPath являются образцами выбора. Единственные выражения XPath, которые могут быть образцами, — это выражения, возвращающие набор узлов (даже набор, состоящий из одного узла) и использующие пути, которые задают только дочерние узлы или узлы атрибутов.
Образцы выбора определены в самой рекомендации XSLT, в то время как выражения XPath определены в рекомендации XPath (www.w3.org/TR/xpath); тем не менее, эти рекомендации совместимы, потому что все образцы выбора являются одновременно выражениями XPath.
СОЗДАНИЕ ПОЛНЫХ ВЫРАЖЕНИЙ XPATH
В главе 7 «Работа и изучение XPath» показано, как создавать полные выражения XPath. Полные выражения XPath можно применять в XSLT в следующих местах: в атрибуте select элементов
Чтобы еще больше все усложнить, следует сказать, что выражения XPath можно использовать в специальной, необязательной части (и только в этой части) образцов выбора: в предикатах. Как мы увидим в этой главе, предикаты — это выражения XPath, которые вычисляются либо в значения «истина/ложь», либо в числа, заключаемые в квадратные скобки, [ и ]. Например, образец
PLANET[NAME="Venus"]
выбирает дочерние узлы
контекстного узла, у которых есть дочерние узлы
с текстом «Venus». Выражения внутри [ и ] представляют собой настоящие выражения XPath с известными ограничениями, которые будут рассмотрены в этой главе.
Безусловно, для создания образцов выбора необходим опыт, поэтому в данной главе приводится много примеров.
MICROSOFT И НЕСТАНДАРТНЫЕ ОБРАЗЦЫ ВЫБОРА
Microsoft поддерживает образцы выбора в своем процессоре XML MSXML3, но есть еще кое-что, о чем вам следует знать: с образцами выбора Microsoft использует также весьма много нестандартного, не используемого W3C синтаксиса. В этой главе я собираюсь придерживаться официальной, W3C, версии, и если вам доведется читать документацию Microsoft об образцах выбора, имейте в виду, что многое из этой документации относится только к реализации Microsoft.
Как вы уже видели, выбрать корневой узел можно при помощи образца выбора «/», как, например:
Можно выбирать элементы, просто задавая их имена, как мы уже видели. Следующий шаблон выбирает элементы
:
При доступе к дочернему узлу определенного узла для разделения имен элементов можно использовать операцию шага /. Пусть, например, требуется создать правило, которое должно применяться только к тем элементам
, которые являются дочерними для элементов
. Для этого можно задать выражение "PLANET/NAME
". Вот правило, окружающее текст таких элементов в элемент HTML <Н3>
:
Можно также использовать символ * в качестве символа-подстановки, что соответствует любому элементу. (* может выбирать только элементы, однако образец @* выбирает любой атрибут.) Например, следующее правило применяется ко всем элементам
, которые являются внуками элементов
:
В предыдущем разделе при помощи выражения "
PLANET/NAME
" я выбирал все элементы
, являющиеся прямыми потомками элементов
, а при помощи выражения "PLANET/*/NAME
" — все элементы
, являющиеся внуками элементов
. Есть, однако, более простой способ выполнить оба выбора — применить выражение "PLANET//NAME
", выбирающее все элементы
, находящиеся внутри элементов
, независимо от глубины вложенности (соответствующие элементы называются потомками элемента
). Иными словами, "PLANET//NAME
" выбирает "PLANET/NAME
", "PLANET/*/NAME
", "PLANET/*/*/NAME
" и т.д.:
Как было показано в главе 3, «Создание и применение шаблонов», можно выбирать атрибуты, если предварять их имена префиксом @. Вы уже работали с атрибутом
UNITS
, который поддерживают большинство детей элементов
:
Earth
1
1
2107
1
128.4
Чтобы извлечь единицы измерения и отобразить их вместе со значениями массы и т.п., можно выбрать атрибут
UNITS
при помощи @UNITS
(листинг 4.1).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
.
.
.
ТD>
Теперь результирующая HTML-таблица включает не только значения, но и их единицы измерения:
The Planets Table
The Planets Table
.
.
.
Mercury
.0553 (Earth = 1)
1516 miles
Venus
.815 (Earth = 1)
3716 miles
.
.
.
Для выбора всех атрибутов элемента можно также использовать подстановку. Например, "
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
, который выбирает элементы
, дочерние по отношению к родителю
.
Таким образом, чтобы понять работу образцов, вам необходимо понять работу образцов шага, поскольку образцы состоят из одного или более образцов шага, в таких выражениях, как "
step-pattern1/step-pattern2/step-pattern3
…". А чтобы понять работу образца шага, необходимо понять работу деятельности трех составных частей — осей, условий узлов и предикатов, которыми мы и займемся в следующих разделах.
Оси — первая часть образцов шага. Например, в образце шага
child::NAME
, ссылающемся на элемент
, дочерний по отношению к контекстному узлу, child
называется осью. У образцов две оси:
• ось
attribute
содержит атрибуты контекстного узла;
• ось
child
содержит детей контекстного узла. Если ось явно не задана, ось child
будет осью по умолчанию.
При помощи осей можно задать шаг расположения (location path) или путь, как в следующем примере, в котором ось
child
используется для задания выбора дочерних узлов контекстного узла, элемента
:
Рассмотрим ряд примеров применения осей:
•
child::PLANET
. Возвращает дочерние элементы
контекстного узла;
•
child::*
. Возвращает все дочерние элементы контекстного узла (* выбирает только элементы);
•
attribute::UNITS
. Возвращает атрибут UNITS
контекстного узла;
•
child::*/child::PLANET
. Возвращает всех внуков
контекстного узла.
Хотя, судя по этим примерам, кажется, что можно применять только оси детей и атрибутов, на практике это не совсем так. Когда требуется указать детей, возможности оси
child
несколько ограничены, потому что необходимо указывать каждый уровень, который необходимо выбрать — например "child::PLANETS/child::PLANET/child::MASS
" выбирает элемент
, дочерний по отношению к элементу
, который, в свою очередь, дочерний по отношению к
. Если вам требуется выбрать все элементы
, появляющиеся в любом месте элемента
, детей, внуков, правнуков и т.д., кажется, что нет способа сделать это в одном образце. В XPath это можно сделать при помощи выражения наподобие "child::PLANETS/descendant::MASS
", но в образцах нельзя использовать ось потомков (descendant). Помните, однако, что в этих же целях можно применить операцию //
. Например, образец "child::PLANETS//child::MASS
" выбирает все элементы
в любом месте внутри элемента
.
Следующий пример (листинг 4.2) демонстрирует работу этого образца, заменяя текст во всех элементах
независимо от того, где они находятся внутри элемента
, на текст "Very heavy!
". Для того чтобы скопировать в результирующий XML-документ все остальные узлы planets.xml
, я также установил правило, выбирающее любой узел при помощи условия узла (node test) node
, с которым мы познакомимся позже. Заметьте, что, хотя образец, выбирающий любой узел, также выбирает все элементы
, образец "child::PLANETS//child::MASS
" гораздо более специален — поэтому, как объяснялось в главе 3, процессор XSLT задаст ему более высокий приоритет для элементов
.
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Very heavy!
А вот результирующий XML-документ:
Mercury
Very heavy!
58.65
1516
43.4
Earth
Very heavy!
1
2107
1
128.4
При задании осей в образцах можно воспользоваться рядом сокращений, применяемых практически повсеместно.
Для образцов существует два правила сокращения осей:
•
child::childname
может быть сокращено как childname
;
•
attribute::childname
может быть сокращено как @childname
.
В следующем списке перечислен ряд примеров образцов с сокращенным синтаксисом; в конце главы вы увидите много других.
•
PLANET
. Выбирает дочерние элементы
контекстного узла;
• *. Выбирает все дочерние элементы контекстного узла;
•
@UNITS
. Выбирает атрибут UNITS
узла;
•
@*
. Выбирает все атрибуты контекстного узла;
•
*/PLANET
. Выбирает всех внуков
контекстного узла;
•
//PLANET
. Выбирает всех потомков
корня документа;
•
PLANETS//PLANET
. Выбирает все элементы
, являющиеся потомками дочерних элементов
контекстного узла;
•
//PLANET/NAME
. Выбирает все элементы
, дочерние по отношению к
;
•
PLANET[NAME]
. Выбирает детей
контекстного узла, у которых есть дочерние элементы
.
В таком образце, как "
child::PLANET
", "child
" является осью, a "PLANET
" — условием узла, что представляет собой вторую часть образцов шага.
Условия узла (node test) составляют вторую часть образцов шага. В качестве условий узла можно использовать названия узлов или символ подстановки
*
для выбора и узлов, и их типов. Например, выражение child::*/child::NAME
выбирает все элементы
, являющиеся правнуками контекстного узла.
Помимо названий узлов и символа подстановки, можно применять также следующие условия узлов:
•
comment()
выбирает узлы комментария;
•
node()
выбирает узел любого типа;
•
processing-instruction()
выбирает узел инструкции обработки. В скобках можно указать название выбираемой инструкции обработки;
•
text()
выбирает текстовый узел.
В следующих разделах мы изучим эти условия узлов и рассмотрим примеры их применения.
Текст комментариев можно выбрать при помощи образца
comment()
. Разумеется, не следует хранить данные, которые попадут в выходной документ, в комментариях входного документа. Тем не менее, вам может потребоваться преобразовать комментарии из формы
в какую-то другую форму, используемую другим языком разметки, — например, элемент
.
В следующем примере я извлеку комментарии из
planet.xml
и включу их в полученные выходные данные.
Venus
"(Earth = 1)">.815
116.75
3716
.943
66.8
Чтобы извлечь комментарии и поместить их в элементы
, я включил правило только для комментариев (листинг 4.3).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Вот результат для Венеры, в котором комментарий преобразован в элемент
:
Venus
.815
116.75
3716
.943
66.8B перигелииСОММЕNT>
Обратите внимание: здесь текст для других элементов в элементе
также включается в выходной документ, потому что так установлено в соответствии с правилом по умолчанию для каждого элемента. Поскольку для элементов я не предоставил какого-либо правила, их текст просто включается в выходной документ.
В образце условие узла
node
выбирает любой узел, за исключением корневого узла — помните, что в действительности это child::node()
. Предположим, мы хотим создать таблицу стилей, копирующую произвольный документ XML, используя
. (В главе 3 для этого применялся элемент
.) Можно начать так, как показано в следующем примере. В этом случае в применяемом шаблоне для выбора любого элемента или любого атрибута используется операция OR, с которой мы познакомимся позже в этой главе (этот шаблон фактически выбирает себя — для того чтобы продолжать копирование на много уровней вглубь):
xmlns:xsl=http://www.w3.org/1999/XSL/Transform">
Однако посмотрите на результат — обратите внимание на то, что в этой версии, выбирающей только элементы и атрибуты (
@*|*
), не копируются узлы-разделители и текстовые узлы:
Это, конечно, неполно. Если я, с другой стороны, буду выбирать по образцу "
@*|node()
" вместо "@*|*
", новое правило шаблона выберет все узлы за исключением корневого узла (который создается в результирующем дереве автоматически), поэтому символы-разделители будут скопированы, так же как и текст (листинг 4.4).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Новый результат:
Mercury
.0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
Venus
.815
116.75
3716
.943
66.8
Earth
1
2107
1
128.4
Следующее выражение выбирает элементы
с атрибутом COLOR
:
.
.
.
А что, если нам требуется выбрать планеты, у которых атрибут
COLOR
имеет значение "BLUE
" (голубой)? Это можно сделать при помощи операции =, как показано в листинге 4.5.
<"xml version="1.0"?>
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The is blue.
Таблица стилей из листинга 4.5 отбирает планеты с голубым цветом и убирает остальные, выключая правило по умолчанию для текстовых узлов. Результат следующий:
The Earth is blue.
Предикаты — настоящие выражения XPath, и XPath гораздо ближе к настоящему языку, чем образцы: к примеру, выражения XPath могут возвращать не только списки узлов, но также логические, строковые и числовые значения. Выражения XPath могут работать не только с текущим узлом или дочерними узлами: можно работать с родительскими узлами, узлами-предками и другими узлами.
Глава 7 полностью посвящена XPath, но имеет смысл предоставить введение в предмет здесь, при обсуждении образцов, потому что часть предиката образца обладает наибольшими возможностями. В предикатах могут быть все виды выражений; в следующем списке перечислен ряд возможных типов, которые будут изучены в следующих разделах:
• наборы узлов;
• логические выражения;
• числа;
• строки.
Набор узлов (node set), как понятно из названия, представляет собой просто совокупность узлов (и может содержать только один узел). Выражение
child::PLANET
возвращает набор узлов, состоящий из всех элементов
. Выражение child::PLANET/child::NAME
возвращает список узлов, состоящий из всех элементов
, дочерних по отношению к элементам
. Для выбора узла или узлов из набора узлов воспользуйтесь следующими функциями для работы с наборами узлов в предикатах:
•
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()
.
xmlns:xsl="http://www.w3.org/1999/XSL/Transform>
The Planets
Для вычисления логических результатов true/false можно применять следующие логические операции XPath:
• != означает «не равно»;
• < означает «меньше, чем» (в документах XML или XSL используйте <);
• <= означает «меньше или равно» (в документах XML или XSL используйте <=);
• = означает «равно» (программисты на С, С++, Java и JavaScript, обратите внимание: эта операция пишется как один знак =, а не два);
• > означает «больше, чем»;
• >= означает «больше или равно».
ИСПОЛЬЗОВАНИЕ СИМВОЛА <
Особенно обратите внимание на то, что непосредственно в документах XML или XSL нельзя использовать символ <, необходимо использовать ссылку на сущность <.
Для связи логических выражений логическими операциями And и Or используются ключевые слова and и or; слово not инвертирует логический смысл выражения — с истины на ложь или со лжи на истину.
В листинге 4.7 я определяю элемент
Земли и помещаю в таблицу строки "Earth
", "needs
", "no
" и "introduction
" вместо числовых данных Земли. Я определяю, которая из планет есть Земля, при помощи предиката "[NAME='Earth']"
, проверяющего значение элемента
, которое, в свою очередь, представляет собой заключенный в элементе текст. Я также предоставил шаблон для других планет, удовлетворяющих предикату "[NAME!='Earth']
''.
Листинг 4.7. Определение планеты Земля
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Earth
needs
no
introduction.
Вот результат:
The Planets Table
<Н1>
The Planets Table
Н1>
Name
Mass
Radius
Day
.
.
.
Earth
needs
no
introduction.
Результат можно увидеть на рис. 4.1.
Рис. 4.1. Применение предикатов XPath
В следующем примере используется логическая операция >. Это правило применяется ко всем элементам
после позиции 5:
Имеется также функция
true
, всегда возвращающая значение true, и функция false
, всегда возвращающая значение false. Функция not
инвертирует логический смысл выражения, как в следующем случае, где я выбираю все элементы
, кроме последнего:
Наконец, функция
lang
возвращает истину или ложь в зависимости от того, является ли язык контекстного узла (определяемый атрибутами xml:lang
) таким же, как язык, который передан в эту функцию.
В XPath числа хранятся в формате числа с плавающей точкой двойной точности. (Технически все числа XPath хранятся в 64-разрядном формате IEEE числа с плавающей точкой двойной точности, floating-point double.) Все числа хранятся как числа с двойной точностью — даже целые числа, как 5 в рассматриваемом примере:
Над числами можно производить ряд операций:
•
+
сложение;
•
-
вычитание;
•
*
умножение;
•
div
деление (символ /, соответствующий делению в других языках, в XML, XSL и XPath уже занят);
•
mod
возвращает значение деления по модулю двух чисел (остаток после деления первого числа на второе).
Например, элемент
вставит в выходной документ строку "600
". В следующем примере выбираются все планеты, у которых отношение дня (измеренного в днях Земли) к массе (где масса Земли принята за 1) больше 100:
XPath также поддерживает следующие функции работы с числами:
•
ceiling()
. Возвращает наименьшее целое, большее, чем переданное функции число;
•
floor()
. Возвращает наибольшее целое, меньшее, чем переданное функции число;
•
round()
. Округляет переданное число до ближайшего целого;
•
sum()
. Возвращает сумму переданных функции чисел.
Например, среднюю массу планет в
planets.xml
можно найти так, как в листинге 4.8:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The average planetary mass is:
В XPath строки формируются из символов Unicode, как можно было предположить. Ряд функций специально предназначен для работы со строками:
•
string(object object1)
. Преобразует объект в строку;
•
starts-with(string string1, string string2)
. Возвращает истину, если первая строка начинается (starts with) со второй строки;
•
contains(string string1, string string2)
. Возвращает истину, если первая строка содержит (contains) вторую строку;
•
substring(string string1, number offset number length)
. Возвращает length
символов из строки, начиная со смещения offset
;
•
substring-before(string string1, string string2)
. Возвращает часть строки string1
до первого вхождения строки string2
;
•
substring-after(string string1, string string2)
. Возвращает часть строки string1
после первого вхождения string2
;
•
string-length(string string1)
. Возвращает количество символов в строке string1
;
•
normalize-space(string string1)
. Возвращает строку string1
после отбрасывания лидирующих и завершающих символов-разделителей и замены нескольких последовательных разделителей на один пробел;
•
translate(string string1, string string2, string string3)
. Возвращает строку string1
, в которой все вхождения символов в строке string2
заменены на соответствующие символы в строке string3
;
•
concat(string string1, string string2, ...)
. Возвращает конкатенацию (объединение) всех строк.
Есть еще одна строковая функция, о которой вам следует знать, входящая не в XPath, а в XSLT:
•
format-number(number number1, string string2, string string3)
. Возвращает строку, содержащую число number1
в виде форматированной строки, используя string2
в качестве форматирующей строки (форматирующие строки создаются так же, как для метода Java java.text.DecimalFormat) и string3
как возможную строку локализации.
В листинге 4.9 я выбираю текстовые узлы, в которых текст начинается с 'Е', чтобы выбрать Earth (Земля), и добавляю текст '(the World)' (мир), получая 'Earth (the World)'. Для этого я применяю предикат "
text()[starts-with(., 'Е')]
".
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
ТD>
(the World)
.
.
.
А вот результат — заметьте, что заголовок для Земли стал "Earth (the World)":
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Earth (the World)
1 (Earth = 1)
2107 miles
1 days
Этот документ показан на рис. 4.2.
Рис. 4.2. Применение текстовых предикатов
XSLT 1.0 добавляет к поддерживаемым XPath типам данных фрагменты результирующего дерева. Это фрагменты дерева, которые можно присваивать переменным XSLT, они не очень широко распространены. Практически все, что можно с ними сделать, — это вычислить их строковое значение. В рабочем проекте XSLT 1.1 их поддержка была удалена, поэтому, видимо, в XSLT 2.0 их уже не будет.
Выражения предикатов можно сокращать, опуская "
position()=
". Например, [position()=3]
становится [3]
, [position()=last()]
становится [last()]
и т.д. С использованием сокращенного синтаксиса применять выражения XPath в предикатах становится существенно проще. Вот ряд примеров:
•
PLANET[2]
. Возвращает второго ребенка
контекстного узла;
•
PLANET[last()]
. Возвращает последнего ребенка
контекстного узла;
•
/PLANETS/PLANET[2]/NAME[1]
. Возвращает первый элемент
второго элемента
элемента
;
•
PLANET[5][@UNITS="million miles"]
. Возвращает пятого ребенка
контекстного узла, только если у него имеется атрибут UNITS
со значением "million miles
". Это выражение можно также написать как PLANET[@UNITS="million miles"][5]
.
На этом мы заканчиваем рассмотрение трех частей образцов шага: осей, условий узлов и предикатов. Это строительные блоки образцов выбора. Лучше всего изучить создание образцов на примере, и многие примеры мы вскоре рассмотрим. Сначала, однако, важно рассмотреть две небольшие темы. Как вы помните из формального определения образцов выбора, можно помимо образцов шага, создавать образцы, выбирающие элементы по идентификатору (ID) или ключу.
В дополнение к созданию образцов из образцов шага, задающих ось, условие узла и предикат, можно применять и образец
id()
для выбора элементов с определенным значением ID. Для работы с этим образцом необходимо задать элементам атрибут ID, который должен быть объявлен с типом ID, что можно сделать в DTD или схеме документа. В следующем примере правило добавляет текст всех элементов, имеющих ID "favorite
":
Вот как может выглядеть объявление DTD для
planets.xml
, в котором объявляется ID и его значение устанавливается в "favorite
":
id ID #REQUIRED>
]>
Mercury
.0553
58.65
1516
.983
43.4
.
.
.
Ряд процессоров XSLT не может осуществлять выбор по ID, потому что они не читают DDS или схему XML. (Возможность доступа к информации ID должна быть включена в XSLT 2.0.) Но есть альтернатива: можно осуществлять выбор по ключу.
ВОЗМОЖНАЯ ПОДДЕРЖКА IDREF
Помимо упрощения работы с ID, в XSLT 2.0 W3C даже рассматривает возможность включения поддержки IDREF. В частности, по заданному ID процессор XSLT может предоставить список всех элементов с атрибутом IDREF или IDREFS, ссылающихся на этот ID. (Заметьте, что сейчас это тоже можно сделать при помощи элемента
Ключи дают простой способ идентифицировать элементы; конкретные ключи можно выбрать при помощи образца "
key()
". Работа с ключами подробно обсуждается в главе 9, но здесь я также приведу небольшой пример.
Для создания ключа служит элемент
. Это элемент верхнего уровня, поэтому он используется вне шаблонов и как дочерний элемент
. В следующем примере я при помощи ключа выбираю планеты, чей атрибут COLOR
(цвет) установлен в "BLUE
" (голубой), что означает Землю:
.
.
.
Earth
1
1
2107
1
128.4
Теперь я могу создать ключ с именем
COLOR
, который выбирает элементы
, проверяя их атрибут COLOR
:
xmlns:xsl="http.//www.w3.org/1999/XSL/Transform">
.
.
.
Теперь при помощи образца "
key()
" можно выбрать элементы
с атрибутом COLOR
со значением "BLUE
":
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name
Mass
Radius
Day
.
.
.
А вот результат — как видите, единственной планетой, удовлетворявшей используемому образцу, была Земля (Earth).
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Earth
1 (Earth = 1)
2107 miles
1 days
При помощи операции Or (или), |, можно осуществлять выбор по нескольким возможным образцам, что очень удобно с ростом сложности документов. В следующем примере я хочу отобразить элементы
и
полужирным, что задается тегом HTML <В>
. Для выбора либо элементов
, либо
в новом правиле я использую операцию Or:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
Вот результаты; заметьте, что значения имени и массы заключены в элементы
<В>
:
The Planets Table
.
.
.
Mercury
.0553
1516 miles
58.65 days
Venus
.815
3716 miles
116.75 days
Earth
1
2107 miles
1 days
Операцию | можно применять к любым допустимым образцам — например, в таких выражениях, как "
PLANET|PLANET//NAME
"; можно использовать несколько операций | — например, "NAME|MASS|DAY
" и т.п.
Изучать образцы лучше всего на примерах. Предположим, что нам требуется преобразовать
planets.xml
в planets.html
, но сохранить только первую планету, Меркурий. Это можно сделать при помощи предиката [position()<2]
, так как позиция первой планеты равна 1, следующей — 2, и т.д. Заметьте, однако, что <; является управляющим символом для процессоров XSLT, начинающим разметку, поэтому вместо < необходимо использовать <
. И отметьте, что для того чтобы убрать из planets.xml
другие элементы, для них нужно предоставить пустой шаблон, что можно сделать при помощи предиката [position()>=2]
(листинг 4.10).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Вот результирующий документ — отметьте, что сохранилась только первая планета, Меркурий:
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Mercury
.0553 (Earth = 1)
1516 miles
58.65 days
В следующем примере в элемент
Земли были добавлены атрибуты COLOR
(цвет) и POPULATED
(населена):
Earth
1
1
2107
1
1284
Как выбрать только элементы, имеющие оба атрибута,
COLOR
и POPULATED
? Можно применить предикат "[@COLOR and @POPULATED]
". Чтобы убрать другие элементы — так, чтобы правило по умолчанию не поместило их текст в результирующий документ, — можно применить предикат "[not(@COLOR) or not(@POPULATED)]
", как показано в листинге 4.11.
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Colorful, Populated Planets
Colorful, Populated Planets
Name
Mass
Radius
Day
А вот результат:
Colorful, Populated Planets
Colorful, Populated Planets
Name
Mass
Radius
Day
Earth
1 (Earth = 1)
2107 miles
1 days
Этот документ показан на рис. 4.3.
Рис. 4.3. Применение предикатов XPath для проверки атрибутов
В следующем примере я копирую
planets.xml
в новый XML-документ и изменяю текст в элементе
Венеры на "The Planet of Love
" (планета любви). Для этого я сначала копирую в результирующий документ все узлы и атрибуты:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
Теперь я добавлю новое правило, выбирающее элементы
с текстом "Venus
" (Венера) по образцу "NAME[text()='Venus']
". Хотя элементы
удовлетворяют обоим правилам этой таблицы стилей, правило с образцом "NAME[text()='Venus']
" осуществляет более узкий выбор, поэтому для элемента
Венеры процессор XSLT применит его:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planet of Love
И вот результат:
Mercury
.0553
58.65
1516
.983
43.4
The Planet of Love
.815
116.75
3716
.943
66.8
Earth
1
1
2107
1
128.4
В действительности, в выражениях XPath можно ссылаться на контекстный узел при помощи ".", и значением по умолчанию для узла является его текст, поэтому следующее правило работает точно так же:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planet of Love
Имеет смысл привести как можно больше примеров — примеров XPath или образцов выбора никогда не бывает слишком много. Ниже приведен содержательный ряд примеров образцов выбора:
•
PLANET
выбирает дочерние элементы
контекстного узла;
•
/PLANETS
выбирает корневой элемент
документа;
•
*
выбирает все дочерние элементы контекстного узла;
•
PLANET[3]
выбирает третьего ребенка
контекстного узла;
•
PLANET[last()]
выбирает последнего ребенка
контекстного узла;
•
PLANET[NAME]
выбирает детей
контекстного узла, имеющих детей
;
•
PLANET[DISTANCE]/NAME
выбирает все элементы
элементов
, содержащих по крайней мере один элемент
;
•
PLANET[DISTANCE]/PLANET[DAY]
выбирает все элементы
элементов
, в которых элемент
содержит по крайней мере один элемент
, и элемент
содержит по крайней мере один элемент
;
•
РLANETS[РLАNET/DAY]
выбирает все элементы
, содержащие элементы
как минимум с одним элементом
;
•
PLANET[DISTANCE][NAME]
выбирает все элементы
, имеющие элементы
и
;
•
PLANETS/PLANET[last()]
выбирает последний элемент
в каждом элементе
;
•
*/PLANET
выбирает всех внуков
контекстного узла;
•
/PLANETS/PLANET[3]/NAME[2]
выбирает второй элемент
третьего элемента
элемента
;
•
//PLANET
выбирает всех потомков
корня документа;
•
PLANETS//PLANЕТ
выбирает потомков элемента
детей элемента
контекстного узла;
•
//PLANET/NAME
выбирает все элементы
, дочерние по отношению к родителю
;
•
РLАNETS//PLАNET/DISTАNСЕ//РЕRIНЕLION
выбирает элементы
везде внутри элемента
элемента
, везде внутри элемента
;
•
@UNITS
выбирает атрибут UNITS
контекстного узла;
•
@*
выбирает все атрибуты контекстного узла;
•
*[@UNITS]
выбирает все элементы с атрибутом UNITS
;
•
DENSITY/@UNITS
выбирает атрибут UNITS
в элементах
;
•
PLANET[not(@COLOR) or not(@SIZE)]
выбирает элементы
, не имеющие обоих атрибутов COLOR
и SIZE
;
•
PLANETS[@STAR="Sun"]//DENSITY
выбирает любой элемент
с элементом-предком
, имеющим атрибут STAR
со значением "Sun
";
•
PLANET[NAME="Venus"]
выбирает детей
контекстного узла, имеющих детей
с текстом "Venus
";
•
PLANET[NAME[1]="Venus"]
выбирает все элементы
, у которых в первом элементе
содержится текст в "Venus
";
•
DAY[@UNITS!="million miles"]
выбирает все элементы
, атрибут UNITS
которых не равен "million miles
";
•
PLANET[@UNITS="days"]
выбирает всех детей
контекстного узла, имеющих атрибут UNITS
со значением "days
";
•
PLANET[6][@UNITS="days"]
выбирает шестого ребенка
контекстного узла, только если у этого ребенка есть атрибут UNITS
со значением "days
", — что также можно записать как PLANET[@UNITS="days"][6]
;
•
PLANET[@COLOR and @UNITS]
выбирает всех детей
контекстного узла, имеющих оба атрибута COLOR
и UNITS
;
•
*[1][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/@UNITS=@REFERENCE]
выбирает все элементы
, у которых значение атрибута REFERENCE
такое же, как значение атрибута UNITS
элемента PLANETS
в корне документа;
•
PLANET/*
выбирает все дочерние элементы элементов PLANET
;
• PLANET/*/DAY выбирает все элементы DAY — правнуки элементов PLANET, являющиеся детьми контекстного узла;
•
*/*
выбирает элементы-внуки текущего элемента;
•
astrophysics:PLANET
выбирает элемент PLANET
в пространстве имен «astrophysics
»;
•
astrophysics:*
выбирает любые элементы в пространстве имен «astrophysics
»;
•
PLANET[DAY and DENSITY]
выбирает все элементы
, у которых есть по крайней мере один элемент
и один элемент
;
•
PLANET[(DAY or DENSITY) and MASS]
выбирает все элементы
, у которых есть по крайней мере один элемент
или один элемент
, а также по крайней мере один элемент
;
•
PLANET[DAY and not(DISTANCE)]
выбирает все элементы
, у которых есть по крайней мере один элемент
и нет элементов
;
•
PLANET[MASS=/STANDARD/REFERENCE/MASS]
выбирает все элементы
, у которых значение элемента
равно значению элемента ///
.
На этом мы завершаем в данный момент рассмотрение образцов выбора; связанный материал приводится в главе 7 при рассмотрении выражений XPath. Глава 5 начинается с изучения способов работы с данными в XML-документах путем сортировки и принятия решения на основе значений данных.