До сих пор материал книги был достаточно очевиден, за исключением одного: несколько загадочных образцов выбора (match pattern). Мы работали с различными образцами выбора, такими как «/PLANETS» в элементах
The Planets Table
.
.
.
В этой главе мы рассмотрим все необходимое, что нужно знать для создания образцов выбора в XSLT. Образцы выбора применяются в элементах
Образцы выбора можно также применять в атрибуте select таких элементов, как
Образцы выбора являются подмножеством выражений XPath, то есть все образцы выбора являются допустимыми выражениями XPath, но не все выражения XPath являются образцами выбора. Единственные выражения XPath, которые могут быть образцами, — это выражения, возвращающие набор узлов (даже набор, состоящий из одного узла) и использующие пути, которые задают только дочерние узлы или узлы атрибутов.
Образцы выбора определены в самой рекомендации XSLT, в то время как выражения XPath определены в рекомендации XPath (www.w3.org/TR/xpath); тем не менее, эти рекомендации совместимы, потому что все образцы выбора являются одновременно выражениями XPath.
СОЗДАНИЕ ПОЛНЫХ ВЫРАЖЕНИЙ XPATH
В главе 7 «Работа и изучение XPath» показано, как создавать полные выражения XPath. Полные выражения XPath можно применять в XSLT в следующих местах: в атрибуте select элементов <xsl:apply-templates>, <xsl:value-of>, <xsl:for-each>, <xsl:param>, <xsl:variable>, <xsl:with-param>, <xsl:copy-of> и <xsl:sort>; в значениях шаблонов атрибутов; в атрибуте test элементов <xsl:if> и <xsl:when>; в значении атрибута элемента <xsl:number> и в предикатах образцов выбора.
Чтобы еще больше все усложнить, следует сказать, что выражения XPath можно использовать в специальной, необязательной части (и только в этой части) образцов выбора: в предикатах. Как мы увидим в этой главе, предикаты — это выражения XPath, которые вычисляются либо в значения «истина/ложь», либо в числа, заключаемые в квадратные скобки, [ и ]. Например, образец PLANET[NAME="Venus"] выбирает дочерние узлы
Безусловно, для создания образцов выбора необходим опыт, поэтому в данной главе приводится много примеров.
MICROSOFT И НЕСТАНДАРТНЫЕ ОБРАЗЦЫ ВЫБОРА
Microsoft поддерживает образцы выбора в своем процессоре XML MSXML3, но есть еще кое-что, о чем вам следует знать: с образцами выбора Microsoft использует также весьма много нестандартного, не используемого W3C синтаксиса. В этой главе я собираюсь придерживаться официальной, W3C, версии, и если вам доведется читать документацию Microsoft об образцах выбора, имейте в виду, что многое из этой документации относится только к реализации Microsoft.
Выбор корневого узла
Как вы уже видели, выбрать корневой узел можно при помощи образца выбора «/», как, например:
Выбор элементов
Можно выбирать элементы, просто задавая их имена, как мы уже видели. Следующий шаблон выбирает элементы
Выбор дочерних элементов
При доступе к дочернему узлу определенного узла для разделения имен элементов можно использовать операцию шага /. Пусть, например, требуется создать правило, которое должно применяться только к тем элементам
Можно также использовать символ * в качестве символа-подстановки, что соответствует любому элементу. (* может выбирать только элементы, однако образец @* выбирает любой атрибут.) Например, следующее правило применяется ко всем элементам
Выбор потомков элемента
В предыдущем разделе при помощи выражения "PLANET/NAME" я выбирал все элементы
Выбор атрибутов
Как было показано в главе 3, «Создание и применение шаблонов», можно выбирать атрибуты, если предварять их имена префиксом @. Вы уже работали с атрибутом UNITS, который поддерживают большинство детей элементов
Чтобы извлечь единицы измерения и отобразить их вместе со значениями массы и т.п., можно выбрать атрибут UNITS при помощи @UNITS (листинг 4.1).
Листинг 4.1. Выбор атрибутов
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
.
.
.
Теперь результирующая 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…". А чтобы понять работу образца шага, необходимо понять работу деятельности трех составных частей — осей, условий узлов и предикатов, которыми мы и займемся в следующих разделах.
Образцы шага, часть 1: оси образца
Оси — первая часть образцов шага. Например, в образце шага child::NAME, ссылающемся на элемент
• ось attribute содержит атрибуты контекстного узла;
• ось child содержит детей контекстного узла. Если ось явно не задана, ось child будет осью по умолчанию.
При помощи осей можно задать шаг расположения (location path) или путь, как в следующем примере, в котором ось child используется для задания выбора дочерних узлов контекстного узла, элемента
Рассмотрим ряд примеров применения осей:
• child::PLANET. Возвращает дочерние элементы
• child::*. Возвращает все дочерние элементы контекстного узла (* выбирает только элементы);
• attribute::UNITS. Возвращает атрибут UNITS контекстного узла;
• child::*/child::PLANET. Возвращает всех внуков
Хотя, судя по этим примерам, кажется, что можно применять только оси детей и атрибутов, на практике это не совсем так. Когда требуется указать детей, возможности оси child несколько ограничены, потому что необходимо указывать каждый уровень, который необходимо выбрать — например "child::PLANETS/child::PLANET/child::MASS" выбирает элемент
Следующий пример (листинг 4.2) демонстрирует работу этого образца, заменяя текст во всех элементах
Листинг 4.2. Выбор элементов <MASS>
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Very heavy!
А вот результирующий XML-документ:
Very heavy!
Very heavy!
При задании осей в образцах можно воспользоваться рядом сокращений, применяемых практически повсеместно.
Сокращенный синтаксис
Для образцов существует два правила сокращения осей:
• child::childname может быть сокращено как childname;
• attribute::childname может быть сокращено как @childname.
В следующем списке перечислен ряд примеров образцов с сокращенным синтаксисом; в конце главы вы увидите много других.
• PLANET. Выбирает дочерние элементы
• *. Выбирает все дочерние элементы контекстного узла;
• @UNITS. Выбирает атрибут UNITS узла;
• @*. Выбирает все атрибуты контекстного узла;
• */PLANET. Выбирает всех внуков
• //PLANET. Выбирает всех потомков
• PLANETS//PLANET. Выбирает все элементы
• //PLANET/NAME. Выбирает все элементы
• PLANET[NAME]. Выбирает детей
В таком образце, как "child::PLANET", "child" является осью, a "PLANET" — условием узла, что представляет собой вторую часть образцов шага.
Образцы шага, часть 2: условия узла
Условия узла (node test) составляют вторую часть образцов шага. В качестве условий узла можно использовать названия узлов или символ подстановки * для выбора и узлов, и их типов. Например, выражение child::*/child::NAME выбирает все элементы
Помимо названий узлов и символа подстановки, можно применять также следующие условия узлов:
• comment() выбирает узлы комментария;
• node() выбирает узел любого типа;
• processing-instruction() выбирает узел инструкции обработки. В скобках можно указать название выбираемой инструкции обработки;
• text() выбирает текстовый узел.
В следующих разделах мы изучим эти условия узлов и рассмотрим примеры их применения.
Выбор комментариев
Текст комментариев можно выбрать при помощи образца comment(). Разумеется, не следует хранить данные, которые попадут в выходной документ, в комментариях входного документа. Тем не менее, вам может потребоваться преобразовать комментарии из формы в какую-то другую форму, используемую другим языком разметки, — например, элемент
В следующем примере я извлеку комментарии из planet.xml и включу их в полученные выходные данные.
Чтобы извлечь комментарии и поместить их в элементы
Листинг 4.3. Выбор комментариев
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Вот результат для Венеры, в котором комментарий преобразован в элемент
Venus
.815
116.75
3716
.943
66.8
Обратите внимание: здесь текст для других элементов в элементе
Выбор узлов при помощи node()
В образце условие узла node выбирает любой узел, за исключением корневого узла — помните, что в действительности это child::node(). Предположим, мы хотим создать таблицу стилей, копирующую произвольный документ XML, используя
xmlns:xsl=http://www.w3.org/1999/XSL/Transform">
Однако посмотрите на результат — обратите внимание на то, что в этой версии, выбирающей только элементы и атрибуты (@*|*), не копируются узлы-разделители и текстовые узлы:
Это, конечно, неполно. Если я, с другой стороны, буду выбирать по образцу "@*|node()" вместо "@*|*", новое правило шаблона выберет все узлы за исключением корневого узла (который создается в результирующем дереве автоматически), поэтому символы-разделители будут скопированы, так же как и текст (листинг 4.4).
Листинг 4.4. Копирующая таблица стилей
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Новый результат:
Выбор текстовых узлов при помощи text()
Выбрать текст узла можно при помощи образца "text()". Как правило, нет особых причин применять условие узла text. В XSLT существует правило по умолчанию, в соответствии с которым текст текстового узла включается в выходной документ, если этот узел не выбирается какими-либо другими правилами. Если нужно сделать это правило по умолчанию явным, можно поступить, например, так:
Можно перекрыть это правило, не отправляя текст из текстовых узлов в выходной документ, — в том числе так:
Потребность в применении условия текстового узла возникает, например, когда нужно выбрать узлы с определенным текстом. Предикат "NAME[text()='Venus']" выбирает элементы
КАК УБРАТЬ КОММЕНТАРИИ
Ранее мы видели, что образец "@*|node()" (в котором используется операция OR, обсуждаемая позже) выбирает из файла planets.xml все, включая комментарии. Если вы хотите убрать комментарии, воспользуйтесь образцом "@*|*|text()", который сохраняет только элементы, атрибуты и текстовые узлы.
Выбор инструкций обработки
Для выбора инструкций обработки используйте образец processing-instruction():
Found a processing instruction.
Можно также указать, какую именно инструкцию обработки вы хотите выбрать, задав имя инструкции (исключая и ?>), — как в следующем примере, в котором выбирается инструкция обработки :
Found an xml-include processing instruction.
РАЗЛИЧИЕ МЕЖДУ КОРНЕВЫМИ УЗЛАМИ И КОРНЕВЫМИ ЭЛЕМЕНТАМИ
Одна из основных причин различия корневого узла в самом начале документа и корневого элемента заключается в том, что при этом у вас есть доступ к инструкциям обработки и другим узлам в прологе документа.
На этом мы заканчиваем обсуждение возможных условий узлов в образцах шага. Третья и последняя часть образцов шага — предикаты
Образцы шага, часть 3: предикаты
Предикаты, третья часть образцов шага, содержат выражения XPath. Предикат можно заключить в операцию [] и проверить, верно ли заданное условие.
Например, можно проверить:
• значение атрибута в заданной строке;
• значение элемента;
• содержит ли элемент определенного ребенка, атрибут или другой элемент;
• позицию узла в дереве узлов.
Подробнее выражения XPath обсуждаются в главе 7, но здесь будет представлено введение в тему, так как эти выражения можно применять в предикатах образцов.
Выражения XPath более сложны, чем образцы выбора. Если при их создании у вас возникнут затруднения, вам может помочь удобная программа-пример ApplyXPath.java из пакета Xalan, при помощи которой можно применить к документу выражение XPath и посмотреть на результаты. Например, если применить выражение XPath "PLANET/NAME" к planets.xml, будут отображены значения всех элементов
C:\>java ApplyXPath planets.xml PLANET/NAME
Если предикат имеет числовое значение, последнее представляет условие позиции (position test). Например, NAME[1]См. выпущенные издательством книги «XML и Java 2, библиотека программиста» и «XML, справочник». — Примеч. ред.
выбирает первого ребенка
Предикаты являются полными выражениями XPath, хотя на предикаты, используемые в образцах, накладывается два ограничения:
• когда образец используется в атрибуте match, предикат не должен содержать никаких ссылок на переменные XSL (которые обсуждаются в главе 9). Это ограничение не применяется к предикатам, используемым в элементах
• образцы не могут использовать в предикатах функцию XPath current, возвращающую текущий узел. Ее применение ограничено, поэтому обработка зависит от реализации и не зависит от текущего состояния обработки.
В следующем примере образец выбирает элементы
.
.
.
Этот образец выбирает любой элемент с дочерним элементом
.
.
.
Теперь я задал элементам
Следующее выражение выбирает элементы
.
.
.
А что, если нам требуется выбрать планеты, у которых атрибут COLOR имеет значение "BLUE" (голубой)? Это можно сделать при помощи операции =, как показано в листинге 4.5.
Листинг 4.5. Применение операции =
<"xml version="1.0"?>
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The
Таблица стилей из листинга 4.5 отбирает планеты с голубым цветом и убирает остальные, выключая правило по умолчанию для текстовых узлов. Результат следующий:
The Earth is blue.
Создание предикатов
Предикаты — настоящие выражения XPath, и XPath гораздо ближе к настоящему языку, чем образцы: к примеру, выражения XPath могут возвращать не только списки узлов, но также логические, строковые и числовые значения. Выражения XPath могут работать не только с текущим узлом или дочерними узлами: можно работать с родительскими узлами, узлами-предками и другими узлами.
Глава 7 полностью посвящена XPath, но имеет смысл предоставить введение в предмет здесь, при обсуждении образцов, потому что часть предиката образца обладает наибольшими возможностями. В предикатах могут быть все виды выражений; в следующем списке перечислен ряд возможных типов, которые будут изучены в следующих разделах:
• наборы узлов;
• логические выражения;
• числа;
• строки.
Предикаты: наборы узлов
Набор узлов (node set), как понятно из названия, представляет собой просто совокупность узлов (и может содержать только один узел). Выражение child::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
xmlns:xsl="http://www.w3.org/1999/XSL/Transform>
The Planets
Вот результат. Как видите, планеты перенумерованы:
The Planets
1. Mercury
2. Venus
3. Earth
Можно также применять функции для работы с наборами узлов в предикатах, как, например, PLANET[position()=last()], выбирающая последнего ребенка
Предикаты: логические значения
В выражениях XPath можно также использовать логические (Boolean) значения. Для чисел ноль принимается за ложь (false), другие значения — за истину (true). Пустая строка, "", также считается ложью, все остальные строки — истиной.
Для вычисления логических результатов true/false можно применять следующие логические операции XPath:
• != означает «не равно»;
• < означает «меньше, чем» (в документах XML или XSL используйте <);
• <= означает «меньше или равно» (в документах XML или XSL используйте <=);
• = означает «равно» (программисты на С, С++, Java и JavaScript, обратите внимание: эта операция пишется как один знак =, а не два);
• > означает «больше, чем»;
• >= означает «больше или равно».
ИСПОЛЬЗОВАНИЕ СИМВОЛА <
Особенно обратите внимание на то, что непосредственно в документах XML или XSL нельзя использовать символ <, необходимо использовать ссылку на сущность <.
Для связи логических выражений логическими операциями And и Or используются ключевые слова and и or; слово not инвертирует логический смысл выражения — с истины на ложь или со лжи на истину.
В листинге 4.7 я определяю элемент
Листинг 4.7. Определение планеты Земля
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
Вот результат:
The Planets Table
<Н1>
The Planets Table
Н1>
Name | Mass | Radius | Day |
Earth | needs | no | introduction. |
Результат можно увидеть на рис. 4.1.
Рис. 4.1. Применение предикатов XPath
В следующем примере используется логическая операция >. Это правило применяется ко всем элементам
Имеется также функция true, всегда возвращающая значение true, и функция false, всегда возвращающая значение false. Функция not инвертирует логический смысл выражения, как в следующем случае, где я выбираю все элементы
Наконец, функция lang возвращает истину или ложь в зависимости от того, является ли язык контекстного узла (определяемый атрибутами xml:lang) таким же, как язык, который передан в эту функцию.
Предикаты: числа
В XPath числа хранятся в формате числа с плавающей точкой двойной точности. (Технически все числа XPath хранятся в 64-разрядном формате IEEE числа с плавающей точкой двойной точности, floating-point double.) Все числа хранятся как числа с двойной точностью — даже целые числа, как 5 в рассматриваемом примере:
Над числами можно производить ряд операций:
• + сложение;
• - вычитание;
• * умножение;
• div деление (символ /, соответствующий делению в других языках, в XML, XSL и XPath уже занят);
• mod возвращает значение деления по модулю двух чисел (остаток после деления первого числа на второе).
Например, элемент
XPath также поддерживает следующие функции работы с числами:
• ceiling(). Возвращает наименьшее целое, большее, чем переданное функции число;
• floor(). Возвращает наибольшее целое, меньшее, чем переданное функции число;
• round(). Округляет переданное число до ближайшего целого;
• sum(). Возвращает сумму переданных функции чисел.
Например, среднюю массу планет в planets.xml можно найти так, как в листинге 4.8:
Листинг 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(., 'Е')]".
Листинг 4.9. Применение функции starts-with
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
.
.
.
А вот результат — заметьте, что заголовок для Земли стал "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]См. выпущенные издательством книги «XML и Java 2, библиотека программиста» и «XML, справочник». — Примеч. ред.
. Возвращает первый элемент
• PLANET[5][@UNITS="million miles"]. Возвращает пятого ребенка
На этом мы заканчиваем рассмотрение трех частей образцов шага: осей, условий узлов и предикатов. Это строительные блоки образцов выбора. Лучше всего изучить создание образцов на примере, и многие примеры мы вскоре рассмотрим. Сначала, однако, важно рассмотреть две небольшие темы. Как вы помните из формального определения образцов выбора, можно помимо образцов шага, создавать образцы, выбирающие элементы по идентификатору (ID) или ключу.
Выбор по ID
В дополнение к созданию образцов из образцов шага, задающих ось, условие узла и предикат, можно применять и образец id() для выбора элементов с определенным значением ID. Для работы с этим образцом необходимо задать элементам атрибут ID, который должен быть объявлен с типом ID, что можно сделать в DTD или схеме документа. В следующем примере правило добавляет текст всех элементов, имеющих ID "favorite":
Вот как может выглядеть объявление DTD для planets.xml, в котором объявляется ID и его значение устанавливается в "favorite":
id ID #REQUIRED>
]>
.
.
.
Ряд процессоров XSLT не может осуществлять выбор по ID, потому что они не читают DDS или схему XML. (Возможность доступа к информации ID должна быть включена в XSLT 2.0.) Но есть альтернатива: можно осуществлять выбор по ключу.
ВОЗМОЖНАЯ ПОДДЕРЖКА IDREF
Помимо упрощения работы с ID, в XSLT 2.0 W3C даже рассматривает возможность включения поддержки IDREF. В частности, по заданному ID процессор XSLT может предоставить список всех элементов с атрибутом IDREF или IDREFS, ссылающихся на этот ID. (Заметьте, что сейчас это тоже можно сделать при помощи элемента <xsl:key> и образца "key()".)
Выбор по ключу
Ключи дают простой способ идентифицировать элементы; конкретные ключи можно выбрать при помощи образца "key()". Работа с ключами подробно обсуждается в главе 9, но здесь я также приведу небольшой пример.
Для создания ключа служит элемент
.
.
.
Теперь я могу создать ключ с именем COLOR, который выбирает элементы
xmlns:xsl="http.//www.w3.org/1999/XSL/Transform">
.
.
.
Теперь при помощи образца "key()" можно выбрать элементы
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
При помощи операции Or (или), |, можно осуществлять выбор по нескольким возможным образцам, что очень удобно с ростом сложности документов. В следующем примере я хочу отобразить элементы
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
Вот результаты; заметьте, что значения имени и массы заключены в элементы <В>:
The Planets Table
.
.
.
Операцию | можно применять к любым допустимым образцам — например, в таких выражениях, как "PLANET|PLANET//NAME"; можно использовать несколько операций | — например, "NAME|MASS|DAY" и т.п.
Примеры образцов
Изучать образцы лучше всего на примерах. Предположим, что нам требуется преобразовать planets.xml в planets.html, но сохранить только первую планету, Меркурий. Это можно сделать при помощи предиката [position()<2], так как позиция первой планеты равна 1, следующей — 2, и т.д. Заметьте, однако, что <; является управляющим символом для процессоров XSLT, начинающим разметку, поэтому вместо < необходимо использовать <. И отметьте, что для того чтобы убрать из planets.xml другие элементы, для них нужно предоставить пустой шаблон, что можно сделать при помощи предиката [position()>=2] (листинг 4.10).
Листинг 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? Можно применить предикат "[@COLOR and @POPULATED]". Чтобы убрать другие элементы — так, чтобы правило по умолчанию не поместило их текст в результирующий документ, — можно применить предикат "[not(@COLOR) or not(@POPULATED)]", как показано в листинге 4.11.
Листинг 4.11. Выбор только элементов с двумя атрибутами COLOR и POPULATED
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-документ и изменяю текст в элементе
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
Теперь я добавлю новое правило, выбирающее элементы
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planet of Love
И вот результат:
The Planet of Love
В действительности, в выражениях 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)] выбирает элементы
• PLANETS[@STAR="Sun"]//DENSITY выбирает любой элемент
• PLANET[NAME="Venus"] выбирает детей
• PLANET[NAME[1]См. выпущенные издательством книги «XML и Java 2, библиотека программиста» и «XML, справочник». — Примеч. ред.
• DAY[@UNITS!="million miles"] выбирает все элементы
• PLANET[@UNITS="days"] выбирает всех детей
• PLANET[6][@UNITS="days"] выбирает шестого ребенка
• PLANET[@COLOR and @UNITS] выбирает всех детей
• *[1]См. выпущенные издательством книги «XML и Java 2, библиотека программиста» и «XML, справочник». — Примеч. ред.
• *[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] выбирает все элементы
• 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-документах путем сортировки и принятия решения на основе значений данных.
="Venus"] выбирает все элементы
[NAME] выбирает любой элемент