Эта глава посвящена принятию решений, сортировке и различным способам обработки данных в ХМL-документах. Мы рассмотрим элементы
,
,
,
,
и
, при помощи которых вы сможете обрабатывать данные и управлять выполнением преобразования в зависимости от значения данных.
Однако эти элементы не предоставляют такой точности, как в языках программирования. Поэтому я также представлю в этой главе расширения XSLT, в том числе элемент рабочего проекта XSLT 1.1
. Этот элемент был предназначен для упрощения применения Java и JavaScript с процессором XSLT. (Для чтения этой книги нет необходимости владеть Java или JavaScript, но если вы знаете эти языки, вам будет приятно удостовериться, что некоторые процессоры XSLT дают возможность использовать их при преобразованиях XML.) Нечто похожее на этот элемент обязательно появится в XSLT 2.0. При помощи расширений вы можете расширять спецификацию XSLT, добавляя в XSLT новые элементы и функции какого-либо производителя или свои собственные.
Кроме того, в этой главе мы также рассмотрим, как перенумеровать элементы в документе, что делать в случае, когда ваш процессор XSLT не поддерживает определенное расширение, и многое другое. Я начну с наиболее часто используемого элемента из рассматриваемых в данной главе:
.
При помощи элемента
осуществляются проверки условия и принимаются действия на основе результата проверки. Он во многом похож на оператор if
в языках программирования. У элемента
один атрибут:
•
test
(обязательный). Устанавливается в значение логического (Boolean
, true/false
) условия, которое вы хотите проверить.
Элемент заключает в себе тело шаблона.
Вот как это работает: вы включаете тело шаблона внутрь элемента
, проверяющего какое-то выражение. Если это выражение истинно, тело шаблона используется, если ложно — игнорируется:
Можно проверять любое выражение XPath. Для преобразования его в значения
true/false
в элементе
применяйте следующие правила:
• если выражение вычисляется в набор узлов, оно трактуется как
true
, когда набор узлов содержит хотя бы один узел;
• выражение-строка считается
true
, если строка не пуста;
• фрагмент результирующего дерева трактуется как
true
, если содержит узлы;
• если результат выражения — число, он считается
true
, когда отличен от нуля.
Элемент
во многом похож на оператор if-then
в языках программирования. Однако не существует оператора
для формирования конструкций if-then-else
— для этого служит элемент
.
В листинге 5.1 я перечисляю планеты в
planets.xml
одну за другой и добавляю горизонтальное правило HTML, элемент
это можно сделать так.
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Planets
is planet number from the sun.
Вот результат — как видите, элемент
Planets
Mercury is planet number 1 from the sun.
Venus is planet number 2 from the sun.
Earth is planet number 3 from the sun.
Рассмотрим еще один пример — преобразование XML-XML, в котором перечисляются планеты из
planets.xml
. Однако я хочу, чтобы выводилось не просто «The first three planets are: Mercury Venus Earth» (первые три планеты: Меркурий Венера Земля), a «The first three planets are: Mercury, Venus, and Earth». Необходимые знаки пунктуации можно добавить, определяя текущий элемент при помощи функции position
и проверяя позицию при помощи
(листинг 5.2).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
The first three planets are:
,
and
.
Вот результат:
The Planets
The first three planets are: Mercury, Venus, and Earth
Как видите, я смог добавить правильные знаки пунктуации, определяя место в документе при помощи
.
При помощи
можно также обнаруживать ошибки во время преобразования. Например, при помощи
можно вывести сообщение, есть ли в planets.xml
элемент
(листинг 5.3).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
The first three planets are:
Each planet must have a name!
.
and
.
Чтобы проверить работу этого кода, я задал один из элементов
пустым:
Mercury
.0553
58.65
1516
.983
43.4
.815
116.75
3716
.943
66.8
.
.
.
Вот как происходит обработка примера в Xalan:
C:\planets>java org.apache.xalan.xslt.Process -IN planets.xml -XSL errors.xsl -OUT new.xml
file:///C:/XSL/w.xsl: Line 18: Column 38: Each planet must have a name!
XSLT Error (javax.xml.transform.TransformerException): Stylesheet directed termination
Если вы знакомы с конструкцией
if
в языках программирования, вы знаете, что инструкция if
обычно сопровождается инструкцией else
, которая выполняется при ложности условия в if
. Но в XSLT нет элемента
. Для задания альтернативных ветвей выполнения XSLT служит элемент
.
Элемент
похож на оператор Java switch
, который позволяет сравнивать значение условия с несколькими возможными вариантами.
У элемента
нет атрибутов. Он содержит один или более элементов
и (не обязательно) один элемент
, который в случае применения должен стоять последним.
Вот как это работает: в элемент
заключаются элементы
, каждый с условием true/false
. Применяется тело шаблона в первом элементе
, чье условие имеет значение true
, все остальные не применяются. Последним элементом внутри элемента
может быть элемент
тело шаблона внутри этого элемента применяется, если ни одно из предыдущих условий
не имело значения true
:
В предыдущем разделе для осуществления этого преобразования нам потребовалось три элемента
:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
The first three planets are:
Each planet must have a name!
,
.
Теперь то же самое можно сделать при помощи единственного элемента
:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
The first three planets are:
Each planet must have a name!
.
.
.
Нам нужно проверить, в каком месте документа мы находимся, при помощи включения нескольких элементов
. У этого элемента только один атрибут:
•
test
(обязательный). Принимает логическое (Boolean
) значение (true/false
) проверяемого условия.
Элемент
содержит тело шаблона.
Атрибут проверки принимает значение true/false выражения, определяющего, будет ли применяться заключенное в элементе
тело шаблона или нет. Например, вот как я добавил элементы
с соответствующими знаками пунктуации для всех планет, кроме последней:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
The first three planets are:
Each planet must have a name!
,
and
.
.
.
Эти два элемента
за исключением последнего, поэтому элемент
можно применить для последнего элемента
. Тело шаблона в этом элементе будет применено, если ни в одном элементе
в элементе
условие не примет значение
.true
У элемента
нет атрибутов, и он содержит тело шаблона. Вот как это выглядит в листинге 5.4.
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
The first three planets are:
Each planet must have a name!
,
and
.
Вот как это работает; этот код дает тот же результат, что и код, проверяющий позицию элементов
при помощи
:
The Planets
The first three planets are: Mercury, Venus, and Earth.
Вот еще один пример преобразования XML-XML. В этом случае я преобразую
planets.xml
в новый XML-документ, сохраняя только название каждой планеты и добавляя описание:
The Planets
Mercury
Hottest
Venus
Hot
Earth
OK
Это преобразование можно реализовать, выбирая значение каждого элемента
, то есть заключенный в нем текст (заметьте, что такого рода строки в XSLT учитывают регистр) (листинг 5.5).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets
Each planet must have a name!
Hottest
Hot
OK
Вот и все.
Предположим теперь, что нам нужно добавить в каждый элемент
атрибут COLOR
:
Mercury
.0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
Earth
1
1
2107
1
128.4
Отобразить названия различных планет при помощи элемента
, отформатированные по-разному при помощи тегов HTML <В>
,
и
в зависимости от значения атрибута COLOR
, можно следующим образом (листинг 5.6).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Planets
<В>
Вот результирующий документ:
Planets
Mercury
Venus
Earth
Как вы видели, при помощи
можно проверять единственное условие, а при помощи
— несколько; аналогичные конструкции присутствуют в большинстве языков программирования. Кроме подобных этим условных операторов, в большей части языков программирования существуют также операторы цикла, и в XSLT содержится нечто похожее — элемент
.
Элемент
позволяет применять тело шаблона в цикле снова и снова для всех элементов набора узлов. С технической точки зрения, он работает с набором узлов, который возвращает выражение XPath и выполняет одно и то же действие с каждым узлом в наборе. При каждом шаге цикла тело шаблона применяется к следующему узлу из набора узлов, что дает возможность легко обрабатывать несколько узлов.
Вы могли заметить, что это описание практически такое же, как и у элемента
У элемента
один атрибут:
•
select
(обязательный). Принимает значение выражения XPath, возвращающее набор узлов, который нужно обработать в цикле.
Элемент может содержать ноль или более элементов
, за которыми следует тело шаблона. Работу с элементом
мы изучим позже в этой главе.
В теле шаблона функция
position
возвращает позицию текущего узла в наборе узлов, a last
возвращает число узлов в наборе. Если
не используется, узлы обрабатываются в порядке документа (в порядке, в котором они перечислены в документе); если же используется элемент
, набор узлов будет сначала отсортирован в порядке, заданном этим элементом.
Предположим, нам нужно отформатировать все названия планет, заключив их в элементы HTML
<Р>
, — это можно сделать следующим образом:
<Р>
Но что делать, если у некоторых планет по два названия, как, например:
Mercury
Closest planet to the sun
.0553
58.65
1516
.983
43.4
Это проблема, поскольку атрибут
select
элемента
сам по себе выберет только первый элемент
. Чтобы пройти в цикле все возможные варианты, вместо него следует применить элемент
(листинг 5.7).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Эта таблица стилей охватывает все элементы
, помещает их значения в элемент <Р>
и добавляет их в выходной документ следующим образом:
Mercury
Closest planet to the sun
Venus
Earth
Вот еще один пример, впервые появившийся в главе 3, «Создание и применение шаблонов», где при помощи элемента
в цикле перебирались все атрибуты элемента:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Следующий пример появился в главе 2, «Создание и применение таблиц стилей». Это упрощенная таблица стилей, в которой нельзя использовать какие-либо элементы высокого уровня, то есть нельзя использовать
или
, однако можно пройти по узлам в цикле при помощи
:
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Эта упрощенная таблица стилей форматирует
planets.xml
в planets.html
практически так же хорошо, как и шаблон, использующий
, в связи с чем появляется интересный вопрос: когда следует для прохода по узлам применять
, а когда
?
Как правило,
хорошо применять в тех случаях, когда организация дочерних узлов неизвестна, и вы хотите применить различные шаблоны к потомкам разных видов — независимо от количества уровней, на которые углубляется их структура. С другой стороны, если дочерние узлы обладают регулярной, хорошо определенной организацией, можно задать
для обработки всех этих узлов.
Элемент
работает во многом так же, как и
; можно даже вкладывать шаблоны при помощи
, как это делается при помощи последовательных элементов
. В листинге 5.8 я прохожу в цикле по каждому элементу
, а затем во вложенном в него цикле по всем элементам, содержащимся в элементе
, перечисляя их данные из элементов <DATA>
следующим образом.
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
Earth
1
1
2107
1
128.4
При помощи элемента
можно сортировать узлы. Этот элемент устанавливает порядок обработки узлов для
и
. В следующем списке перечислены атрибуты
:
•
select
(необязательный). Принимает значение выражения XPath, возвращающего набор узлов для сортировки. По умолчанию — «string(.)»;
•
order
(необязательный). Задает порядок сортировки, устанавливается в «ascending» (по возрастанию) или «descending» (по убыванию);
•
case-order
(необязательный). Определяет, будут ли буквы в верхнем регистре располагаться перед буквами в нижнем регистре. Устанавливается в «upper-first» (сначала верхний) или «lower-first» (сначала нижний);
•
lang
(необязательный). Задает язык, чьи соглашения о сортировке будут применяться. Устанавливается в код языка, допустимый в атрибуте xml:lang
;
•
data-type
(необязательный). Определяет, будет ли сортировка вестись в алфавитном или числовом порядке. Устанавливается в «text
» (текст), «number
» (число) или в QName
.
Этот элемент не включает содержимое. Его следует применять внутри элементов
В листинге 5.9 я только отсортирую элементы
в planets.xml
в возрастающем алфавитном порядке по их именам, используя
в упрощенной таблице стилей.
The Sorted Planets Table
The Sorted Planets Table
Name
Mass
Radius
Day
А вот результат. Обратите внимание на то, что планеты действительно отсортированы как Earth, Mercury и затем Venus:
The Sorted Planets Table
The Sorted Planets Table
Name
Mass
Radius
Day
Earth
1
2107
1
Mercury
.0553
1516
58.65
Venus
.815
3716
116.75
Вид документа показан на рис. 5.1.
Рис. 5.1. Сортировка при помощи упрощенного шаблона
При помощи атрибута select можно указать, что нужно сортировать. Например, таким образом можно отсортировать планеты по плотности (листинг 5.10).
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Planets
Planets sorted by density
Planet
Mass
Day
Density
Вот результаты этого преобразования:
Planets
Planets sorted by density
Н1>
Planet
Mass
Day
Density
Venus
.815
116.75
.943
Mercury
.0553
58.65
.983
Earth
1
1
1
По умолчанию
производит сортировку в алфавитном порядке, что означает, что «10» располагается перед «2». Для осуществления сортировки в числовом порядке установите атрибут типа данных в «number»:
Убывающий порядок сортировки задается установкой у элемента
атрибута order в «descending». Можно также сортировать по значениям атрибута, например:
НОВОЕ В XSLT 2.0
Одним из важных преимуществ XSLT 2.0 является поддержка схем XML, и W3C планирует внести в XSLT 2.0 возможность сортировки по любому типу данных, определенному в схеме документа, так же, как сейчас можно сортировать по строкам или числам.
Стоит отметить, что при сортировке можно применять несколько критериев — для этого просто примените несколько элементов
. Первый элемент
отсортирует данные по основному критерию, следующий элемент — по следующему основному критерию и т.д. Вот, например, как внутри элемента
можно отсортировать планеты сначала по расстоянию, а затем по плотности (при этом планеты с одинаковым расстоянием от Солнца будут отсортированы по плотности):
На этом мы заканчиваем обсуждение сортировки, и я перехожу к сопутствующей теме: нумерации.
При помощи элемента
узлу в результирующем документе присваивается очередной номер. Например, можно перенумеровать абзацы в контракте или строфы в поэме. Можно даже перенумеровать части документа несколькими уровнями, как, например, «Paragraph 3.2.5.1.» и т.п.
Элемент
обладает следующими атрибутами:
•
level
(необязательный). Определяет, как будут присваиваться последовательные числа. Устанавливается в «single» (один), «multiple» (несколько) или «any» (любой). Значение по умолчанию — «single»;
•
count
(необязательный). Определяет, какие узлы нужно подсчитывать. Устанавливается в образец;
•
from
(необязательный). Определяет точку начала отсчета. Устанавливается в образец;
•
value
(необязательный). Форматируемое число;
•
format
(необязательный). Определяет формат вывода. Устанавливается в шаблон значений атрибута, возвращающий строку форматирования;
•
lang
(необязательный). Определяет язык, чьи соглашения следует использовать для нумерации. Устанавливается в код языка, который можно применять в атрибуте xml:lang
;
•
letter-value
(необязательный). Позволяет выбрать различные схемы нумерации. устанавливается в «alphabetical» (алфавитная) или «traditional» (обычная);
•
grouping-separator
(необязательный). Символ для разделения групп разрядов — например, запятая. Устанавливается в шаблон значений атрибутов, возвращающий единственный символ;
•
grouping-size
(необязательный). Количество разрядов в каждой группе — определяет место применения разделителя групп разрядов. Устанавливается в шаблон значений атрибутов, возвращающий число.
СОВЕТ ПО НУМЕРАЦИИ
Как можно видеть из этого списка атрибутов, существует весьма много возможных схем нумерации. Операции нумерации могут стать довольно сложными, поэтому есть один прием: если нумерация будет становиться слишком сложной и запутанной, я просто выведу результирующий документ без нумерации и затем воспользуюсь второй таблицей стилей, которая применит нумерацию.
Существует три основных способа нумерации, в зависимости от установки атрибута уровня (
level
): «single
», «multiple
» или «any
». В следующих разделах мы по очереди рассмотрим каждую из этих схем, начав с одноуровневой нумерации, которая установлена по умолчанию.
Одноуровневая нумерация — это простая нумерация, когда перенумеровываются узлы-братья на одном уровне. Этот тип нумерации установлен по умолчанию. В листинге 5.11 при помощи одноуровневой нумерации перенумеровываются планеты в
planets.xml
.
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
1. Mercury
.0553 (Earth = 1)
1516 miles
58.65 days
2. Venus
.815 (Earth = 1)
3716 miles
116.75 days
3. Earth
1 (Earth = 1)
2107 miles
1 days
Этот результат показан на рис. 5.2.
Рис. 5.2. Одноуровневая нумерация элементов
По умолчанию при нумерации используются числа, но есть и другие возможности. Например, если бы я использовал
, планетам были бы присвоены буквы a, b и с:
The Planets Table
The Planets Table
Name
Mass
Radius
Day
a. Mercury
.0553 (Earth = 1)
1516 miles
58.65 days
b. Venus
.815 (Earth = 1)
3716 miles
116.75 days
.
.
.
Ниже приведены возможные в атрибуте
format
лексемы и соответствующие им виды нумерации:
• 1 порождает последовательность 1, 2, 3…;
• 01 порождает последовательность 01, 02, 03…;
• другие числа Unicode в других системах счисления порождают последовательности аналогично двум предыдущим правилам;
• а порождает последовательность a, b, с…, аа, ab…;
• А порождает последовательность А, В, С…, АА, АВ…;
• i порождает последовательность i, ii, iii, iv…, ix, x, xi, xii…;
• I порождает последовательность I, II, III, IV…, IX, X, XI, XII…
Нумерация на произвольных уровнях
Иногда может потребоваться просто подсчитать узлы определенного типа независимо от их уровня в документе. Например, в различных местах иерархии документа не исключено наличие нескольких элементов
, и вам может быть нужно просто трактовать документ как поток данных, подсчитывая встречающиеся элементы
.
Вот как примерно это будет выглядеть, если в
planets.xml
элементы
встречаются на различных уровнях:
Planets Table
Mercury
.0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
.
.
.
Чтобы сосчитать общее число элементов
, в таблице стилей можно установить атрибут level
в «any» (листинг 5.12).
Листинг 5.12. Нумерация на произвольных уровнях
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
1. Planets Table
Name
Mass
Radius
Day
2. Mercury
.0553 (Earth = 1)
1516 miles
58.65 days
3. Venus
.815 (Earth = 1)
3716 miles
116.75 days
4. Earth
1 (Earth = 1)
2107 miles
1 days
При помощи атрибута
from
можно указать, с какого узла-предка начинать отсчет; например, если установить узел-предок в элемент
так:
то процессор XSLT осуществит обратный просмотр только до первого предка
и начнет нумерацию с этой точки документа.
Многоуровневая нумерация
Элемент
также поддерживает многоуровневую нумерацию — такую как 3.1.2.5 и т. п. Для работы с ней нужно установить атрибут level
в «multiple». При помощи атрибута count
можно указать, узлы какого типа вы хотите нумеровать, установив этот атрибут в образец, например: "PART|CHAPTER|PARAGRAPH
". При обработке элементов
процессор XSLT нумерует узлы в соответствии с иерархией документа.
В примере я нумерую каждый уровень в иерархии элементов
planets.xml
, установив атрибут count
в «*» для выбора всех элементов. Можно также указать формат нумерации при помощи атрибута format
. При многоуровневой нумерации атрибут format
задает формат для различных уровней, например «1.1.1.» задает нумерацию 1., 2., … и т.д. для узлов верхнего уровня, 1.1., 1.2., … и т.д. для узлов уровнем ниже и 1.2.1., 1.2.2., … и т. д. для следующего уровня вниз. Вот как выглядит таблица стилей для этого примера в листинге 5.13.
Листинг 5.13. Многоуровневая нумерация
<хsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Вот результат преобразования planets.xml в новый XML-документ, в котором перенумерованы все уровни элементов в соответствии с иерархией документа:
1.
1.1.
1.1.1. Mercury
1.1.2. .0553
1.1.3. 58.65
1.1.4. 1516
1.1.5. .983
1.1.6. 43.4
1.2.
1.2.1. Venus
1.2.2. .815
1.2.3. 116.75
1.2.4. 3716
1.2.5. .943
1.2.6 66.8
1.3.
1.3.1. Earth
1.3.2. 1
1.3.3. 1
1.3.4. 2107
1.3.5. 1
1.3.6. 128.4
На этом мы завершаем рассмотрение нумерации документов и переходим к последней теме этой главы — расширяемости XSLT.
Расширяемость XSLT
Несмотря на кажущуюся сложность XSLT, он во многих отношениях ограничен по сравнению с языками программирования, и в процессорах XSLT сразу же начали появляться расширения XSLT. Например, Saxon представил элемент
, реализуя в XSLT стандартный для программирования цикл while
(до тех пор, пока). Xalan представил такие элементы, как
, для поддержки вывода нескольких документов. А процессор MSXML3 от Microsoft позволяет писать функции на языках таких сценариев, как JavaScript, и затем вызывать их и выполнять их код.
Можно представить, с каким беспокойством на это смотрит W3C. Его работа, в принципе, заключается в стандартизации работы таких языков, как XSLT, но производители постоянно представляли свои собственные, нестандартные расширения в виде новых элементов и функций. С другой стороны, W3C не может предугадать все новые элементы и функции, поэтому консорциум начал работать над стандартизацией способов включения функций расширения и элементов в XSLT. Расширения должны удовлетворять ряду общих правил:
• расширения должны использовать пространства имен во избежание конфликтов с элементами XSL;
• процессор XSLT должен быть в состоянии распознать применение расширения — и в случае ошибки расширения реагировать хорошо определенным способом;
• таблица стилей должна быть в состоянии проверить, доступно ли определенное расширение, и если нет, вернуться назад.
НОВОЕ В XSLT 2.0
Легко представить сложности W3C даже с этими общими правилами, и комитет XSLT 2.0 собирается исследовать возможность реализации всех расширений на «чистом» XSLT, вообще не прибегая к каким-либо внешним языкам программирования.
W3C разрешил расширения двух видов, главным образом, потому, что они уже были приняты де-факто — функции расширения и элементы расширения. Хотя они пользуются популярностью, это весьма неясная область, поскольку различные производители представили разные способы их реализации.
В XSLT 1.0 проверить доступность функции расширения можно при помощи функции
function-available
, а доступность элемента расширения — при помощи функции element-available
.
XSLT 2.0 готовится определить стандартные средства связывания XSLT с элементами расширения и, вероятно, они будут гораздо лучше проработаны, чем имеющиеся сейчас.
Давайте посмотрим на все это в работе. В следующих разделах мы рассмотрим и функции, и элементы расширения, начав с функций.
ИНИЦИАТИВА EXSLT
Теперь, после того, как механизмы расширения в рабочем проекте XSLT 1.1 были отложены до XSLT 2.0, роль других разнообразных попыток стандартизации расширений XSLT значительно повысилась. Познакомьтесь, например, с EXSLT на www.exslt.org. EXSLT — это инициатива открытого сообщества, работающего над стандартизацией расширений XSLT.
Функции расширения
В XSLT 1.0 W3C определил способ отделения функций расширения от встроенных функций, установив требование, чтобы для обращения к функциям расширения использовались имена с заданным пространством имен, как в
starpowder:calculate()
. В XSLT 1.0 также имеется функция function-available()
для проверки наличия функции по ее имени.
В рабочем проекту XSLT 1.1 на функции расширения были наложены некоторые дополнительные ограничения:
• функции расширения должны работать как встроенные функции;
• для Java и ECMAScript должны быть реализованы привязки к языку;
• механизм должен позволять естественное расширение для поддержки других языков в будущем;
• для реализации переносимой привязки функции расширения для любого конкретного языка не должен быть нужен процессор;
• процессор, реализующий функции расширения для любого языка, чья привязка обеспечивается спецификацией XSLT, должен соответствовать этим языкам;
• должны быть разрешены как встроенные реализации функций расширения, так и внешние;
• в функции расширения должно быть возможно передавать аргументы всех типов данных XPath;
• функции расширения должны иметь возможность возвращать в качестве результата все типы данных XPath;
• функции расширения должны иметь возможность создавать и возвращать наборы узлов фрагментов XML;
• должна иметься возможность включать или импортировать функции расширения из другой таблицы стилей;
• при неоднозначности выбора реализации функции расширения процессор должен выдать ошибку и прекратить работу;
• процессор должен преобразовывать аргументы способом, согласованным со встроенными функциями;
• функции расширения должны быть способны вернуть объект любого типа основного языка;
• должна существовать возможность передать в функцию расширения объект любого типа основного языка.
Вплоть до недавнего времени процессоры XSLT полностью самостоятельно определяли способ реализации функций расширения. Например, в Saxon и Xalan существует возможность непосредственно выполнять код Java, если определить пространство имен, задающее класс Java в качестве последней части URI. Я поступил так в следующем случае, определив пространство имен
Date
, соответствующее классу Java Date
:
xmlns:xsl=http://www.w3.org/1999/XSL/Transform
xmlns:Date="http://www.saxon.com/java/java.util.Date">
.
.
.
После этого я могу воспользоваться такими функциями класса
Date
Java, как toString
и new
, для того чтобы заключить текущую дату в элементы заголовка <Н1>
HTML в выходном документе (листинг 5.14).
Листинг 5.14. Применение функций Date Java
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:Date="http://www.saxon.com/java/java.util.Date">
The Planets Table
The Planets Table
Name
Mass
Radius
Day
Результат применения этой функции приведён на рис. 5.3.
Рис. 5.3. Применение функции расширения
Вот работоспособная схема и заодно веский повод включить Java в XSLT. Тем не менее, в XSLT 1.1 был представлен элемент
, который, по всей видимости, будет включен и в XSLT 2.0.
Элемент
Элемент
был определен в рабочем проекте XSLT 1.1, он предоставляет хорошо определенный способ связи функций расширения с таблицами стилей XSLT. Это элемент верхнего уровня, обладающий следующими атрибутами:
•
implements-prefix
(необязательный). Задает имя пространства имен функции расширения, которую реализует этот элемент. Принимает значение NCNAME
;
•
language
(необязательный). Задает язык, используемый функцией расширения. Устанавливается в «ecmascript
» (стандарт JavaScript), «javascript
», «java
» или QNAME, не являющееся NCNAME;
•
src
(необязательный). Предоставляет URI, в котором реализована функция расширения. Например, это может быть класс Java;
•
archive
(необязательный). Задает архивы, которые необходимо загрузить перед запуском функции расширения, если они есть. Принимает значения списка URI, разделенного символами-разделителями.
Элемент содержит символьные данные (Microsoft использует раздел
CDATA
), реализующие функцию или функции расширения.
Как теперь связать функцию, определенную в элементе
, с вашей таблицей стилей XSLT? Сначала создайте в своей таблице стилей элемент
как элемент верхнего уровня, затем поместите в него функции, которые вы хотите определить. В приведенном ниже примере я определяю две функции JavaScript, makeMoney
(сделать деньги) и makeMoreMoney
(сделать еще больше денег), в элементе
, реализующем пространство имен расширений «starpowder
»:
function makeMoney(e) {
.
.
.
}
function makeMoreMoney(e) {
.
.
.
}
В зависимости от вашего процессора XSLT, может оказаться хорошим решением заключить такого рода сценарии в раздел CDATA:
function makeMoney(e) {
.
.
.
}
function makeMoreMoney(e) {
.
.
.
}
]]>
Теперь при помощи пространства имен «
starpowder
» можно указать, что вызывается функция расширения:
Вот и все (если ваш процессор XSLT это поддерживает). Если вместо сценария вы хотите указать класс Java, воспользуйтесь атрибутом
src
:
РАБОТА С ВНЕШНИМИ РЕСУРСАМИ
Атрибут src также используется, если есть архив подпрограмм JavaScript, как, например, src="archives.js".
Из всех известных мне процессоров XSLT элемент
реализует только процессор Microsoft MSXML3. Информация о работе со сценариями для написания функций расширения для Internet Explorer приведена на web-узле Microsoft (в данный момент это страница по адресу http://msdn.microsoft.com/xml/xslguide/script-overview.asp, но, кажется, Microsoft меняет структуру web-узла каждые два дня или около того).
Следующий пример демонстрирует работу
с Internet Explorer. Я создал функцию JavaScript для преобразования данных о радиусе планет из planets.xml
, приведенных в милях, в километры и последующем выводе этих данных в километрах.
Как обсуждалось в главе 2 в разделе «Преобразование документов XML при помощи Internet. Explorer», для просмотра XML-документа, использующего таблицу стилей XSL, в Internet Explorer, версии 5.5 и младше в документ необходимо внести некоторые изменения (если только вы не установили последний разборщик MSXML или не используете недавно появившуюся версию браузера 6.0, хотя и в этом случае нужно применять «
text/xsl
»). Для начала в таблице стилей XSL используйте тип MIME «text/xsl
», а не «text/xml
». Я также задал URI для таблицы стилей «kilometers.хsl
» следующим образом (листинг 5.15).
Листинг 5.15. Установка использования kilometers.xsl для planets.xml в Internet Explorer
Mercury .0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
.
.
.
Для преобразования таблицы стилей
kilometers.xsl
для работы в IE 5.5 или младше я воспользовался пространством имен XSL, которое использует IE, и добавил элемент
, показав, что я собираюсь писать сценарии на JavaScript. Заметьте, однако, что элемент
в Internet Explorer не поддерживает атрибут префикса реализации, поэтому я не могу связать функции, определенные в пространстве имен:
.
.
.
.
.
.
В соответствии с требованиями Internet Explorer, код должен быть заключен в раздел CDATA. Здесь я определил функцию
milesToKilometers
, которая принимает узел, читает текст узла в свойстве text
и преобразует текст в число миль при помощи функции JavaScript parseInt
. Далее я умножаю число миль на 1,6, чтобы получить километры, и возвращаю результат:
function milesToKilometers(e) {
miles = parseInt(e.text);
return miles * 1.6;
}
]]>
.
.
.
Поскольку пока в Internet Explorer нельзя связать пространство имен с функцией расширения, для их вызова используется специальный элемент Microsoft
. Ниже показано, как это выглядит в таблице стилей kilometers.xsl
, где я передаю в функцию milesToKilometers
текущий узел
для преобразования миль в километры. Поскольку IE 5.5 и младше не поддерживают правила по умолчанию (хотя версия 6.0, вышедшая одновременно с подписанием этой книги в печать, поддерживает их, и вам не нужно ничего менять), для этих браузеров я предоставил правило для корневого узла (листинг 5.16).
Листинг 5.16. kilometers.xsl
function milesToKilometers(e) {
miles = parseInt(e.text);
return miles * 1.6;
}
]]>
The Planets Table
The Planets Table
Name
Mass
Radius
Day
milesToKilometers(this)
Вот и все, результат этого преобразования приведен на рис. 5.4.
Рис. 5.4. Применение функции расширения в Internet Explorer
Со временем производители будут поставлять все больше и больше функций расширения. Как можно определить, доступна ли заданная функция расширения? Для этого служит функция
function-available
.
Применение функции function-available
Функция XSLT 1.0
function-available
служит для проверки доступности функции. В следующем примере я хочу воспользоваться функцией расширения starpowder:calculate
для математических вычислений, а если она недоступна, я отправляю в результирующий документ текст «Sorry, can't do math today.» (Извините, сегодня математические вычисления не работают.), хотя можно, конечно, прекратить обработку и вывести сообщение об ошибке при помощи элемента
:
Sorry, can't do math today.
Внешние объекты
В рабочем проекте XSLT 1.1 для поддержки функций расширения появился новый тип данных — внешний объект (external object). Переменной XSLT, о которой пойдет речь в главе 9, может быть присвоен внешний объект — так же, как и один из четырех типов данных XPath, поддерживаемых в XSLT (строка, число, логическое значение, набор узлов). Внешний объект представляет объект, который создается внешним языком программирования, возвращается функцией расширения и не может быть преобразован в один из четырех типов данных XPath. Тип данных «
external object
» был добавлен в XSLT для того, чтобы предоставить вам безопасную «оболочку» для таких данных. Пока еще никто не реализовал поддержку внешних объектов, но это ожидается в скором времени.
Элементы расширения
Элементы расширения — это элементы, добавленные в XSLT пользователем или производителем. В рабочем проекте XSLT 1.1 для элементов расширения был установлен ряд правил, и в XSLT 2.0 предполагается более широкая их поддержка.
В рабочем проекте XSLT 1.1 правила определяли, что элементами расширения должны быть определенные пользователем или производителем элементы, не являющиеся элементами верхнего уровня. Они должны принадлежать к пространству имен, которое было определено как пространство имен расширений.
Для определения пространства имен расширений применяется атрибут
extension-element-prefixes
в элементе
, или атрибут xsl:extension-element-prefixes
в элементе буквального результата или элементе расширения.
Ниже приведен пример. Xalan позволяет вам создать несколько выходных документов при помощи своего элемента расширения
. Для того чтобы применить этот элемент, я могу добавить в planets.xml
элементу документа атрибут file
, задав имя файла, в который будет отправлен вывод, как redirected.xml
:
Mercury
.0553
58.65
1516
.983
43.4
.
.
.
Теперь в таблице стилей XSLT, которую я назвал
redirect.xsl
, я определяю пространство имен «redirect
» так, чтобы оно соответствовало классу Java, который поддерживает ее в Xalan: org.apache.xalan.lib.Redirect
. Я также устанавливаю атрибут extension-element-prefixes
элемента
в значение пространства имен «redirect
»:
version="1.0"
xmlns:lxslt=http://xml.apache.org/xslt"
xmlns:redirect="org.apache.xalan.lib.Redirect"
extension-element-prefixes="redirect">
.
.
.
В этот момент мне ничто не мешает применить элемент расширения
для записи вывода в новый файл (в отличие от указанного в командной строке). Например, для того, чтобы отправить в другой файл отформатированное содержимое элемента
, я могу получить имя создаваемого файла из атрибута file
элемента
и записать данные в этот новый файл:
version="1.0"
xmlns:lxslt="http://xml.apache.org/xslt"
xmlns:redirect="org.apache.xalan.lib.Redirect"
extension-element-prefixes="redirect">
Операция завершена; вот как это может выглядеть при использовании Xalan в Windows:
C:planets>java org.apache.xalan.xslt.Process -IN planets.xml -XSL redirect.xsl -OUT new.xml
При этом будет создан файл
redirected.xml
, который выглядит следующим образом:
Mercury
.0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
.
.
.
Применение функции element-available
Для проверки доступности элемента служит функция XSLT 1.0
element-available
. В следующем примере я проверяю наличие элемента с названием
:
Sorry, can't do math today.
Есть еще один способ обработать случай отсутствия элемента расширения — элемент
.
Элемент
При помощи элемента XSLT 1.0
можно указать, что следует делать в случае отсутствия элемента расширения. Этот элемент заключается в элемент расширения и используется в случае его недоступности.
У элемента
нет атрибутов, он содержит тело шаблона.
В следующем примере я создам элемент
внутри элемента
из предыдущего примера. В случае отсутствия элемента
элемент
прекратит обработку и выдаст сообщение:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:lxslt="http://xml.apache.org/xslt"
xmlns:redirect="org.apache.xalan.lib.Redirect"
extension-element-prefixes="redirect">
Could not create multiple output documents.
На этом мы заканчиваем введение в работу с данными в документах XML. В следующей главе мы разберем эту тему и узнаем, как изменять содержимое документа и создавать новые элементы, атрибуты и инструкции обработки.
Загрузка...