Функции xslt


Содержание

Функции XSLT

Введение

О XSLT и Sablotron

XSLT (Extensible Stylesheet Language (XSL) Transformations) это язык трансформации XML-документов в другие XML-документы. Это стандарт, определённый консорциумом World Wide Web (W3C). Информацию о XSLT и связанных технологиях можно найти на http://www.w3.org/TR/xslt.

Установка

Это расширение использует Sablotron и expat, которые находятся на http://www.gingerall.com/. Имеются как экзешники, так и исходный код.

В UNIX запустите configure с опциями --enable-xslt --with-xslt-sablot. Библиотека Sablotron должна быть установлена там, где ваш компилятор может её найти.

Об этом расширении

Это расширение PHP предоставляет не зависящий от процессора API для трансформаций XSLT. В настоящее время это расширение поддерживает только библиотеку Sablotron от Ginger Alliance. Планируется поддержка и других библиотек, таких как библиотеки Xalan или libxslt.

Функции xslt

boolean boolean ( object )

Явным образом преобразует объект, который ей передаётся в булевый тип.

boolean not ( boolean )

Выполняет логическое отрицание.

Возвращает true , «истину».

Возвращает false , «ложь».

boolean lang ( string )

Возвращает истину, если идентификатор языка, который передан ей в виде строкового параметра, соответствует языковому контексту контекстного узла.

Числовые функции

number number ( object ?)

Явным образом конвертирует свой аргумент в числовой тип. Если аргумент опущен, то выполняется с множеством, состоящим из контекстного узла.

number sum ( node-set )

Суммирует значения узлов из переданного ей множества.

number floor ( number )

Округляет аргумент до ближайшего не большего целого.

number ceiling ( number )

Округляет аргумент до ближайшего не меньшего целого.

number round ( number )

Округляет аргумент до ближайшего целого значения.

Строковые функции

string string ( object ?)

Преобразует свой аргумент к строковому типу явным образом. Если аргумент опущен, то выполняется с множеством, состоящим из контекстного узла.

string concat ( string , string , string *)

Возвращает конкатенацию (строковое сложение) своих аргументов.

boolean starts-with ( string , string )

Принимает на вход два строковых аргумента и возвращает true , если первая строка начинается со второй и false в противном случае.

boolean contains ( string , string )

Принимает на вход два строковых аргумента и возвращает true , если первая строка содержит вторую и false в противном случае.

string substring-before ( string , string )

Принимает на вход два строковых аргумента, находит в первой строке вторую и возвращает подстроку, которая ей предшествует.

string substring-after ( string , string )

Принимает на вход два строковых аргумента, находит в первой строке вторую и возвращает подстроку, которая за ней следует.

string substring ( string , number , number ?)

Возвращает подстроку переданного ей строкового аргумента, которая начинается с позиции, указанной вторым аргументом и длиной, указанной третьим аргументом. Если третий аргумент не указан, то подстрока продолжается до конца строки.

number string-length ( string ?)

Возвращает число символов строкового аргумента.

string normalize-space ( string ?)

Производит со строковым аргументом нормализацию пробельного пространства. Если аргумент опущен, выполняется со строковым значением контекстного узла.

string translate ( string , string , string )

Производит замену символов первого своего строкового аргумента, которые присутствуют во втором аргументе на соответствующие символы третьего аргумента.

Функции множеств узлов

Возвращает размер контекста вычисления выражения.

Возвращает позицию контекста вычисления выражения.

number count ( node-set )

Возвращает число узлов, которое входит во множество, переданное ей в качестве аргумента.

string local-name ( node-set ?)

Возвращает локальную часть имени первого в порядке просмотра документа узла множества, переданного в качестве аргумента или локальную часть имени контекстного узла, если аргумент отсутствует. Если аргумент опущен, то выполняется с множеством, состоящим из контекстного узла.

string namespace-uri ( node-set ?)

Возвращает URI пространства имён первого в порядке просмотра документа узла множества, переданного в качестве аргумента или локальную часть имени контекстного узла, если аргумент отсутствует. Если аргумент опущен, то выполняется с множеством, состоящим из контекстного узла.

string name ( node-set ?)

Возвращает в виде префикс:имя расширенное имя локальную часть имени первого в порядке просмотра документа узла множества, переданного в качестве аргумента или локальную часть имени контекстного узла, если аргумент отсутствует. Если аргумент опущен, то выполняется с множеством, состоящим из контекстного узла.

node-set id ( object )

Возвращает множество узлов по уникальным идентификаторам.

Другие функции

node-set key ( string , object )

По данному имени и значению ключа возвращает множество узлов, которые им обладают.

node-set document ( object , node-set ?)

Позволяет обращаться к внешним документам по заданным URI. Первый узел необязательного параметра node-set принимается за точку отсчёта для относительных URI.

Возвращает текущий узел преобразования.

string unparsed-entity-uri ( string )

Возвращает URI неразбираемой сущности по её имени.

string generate-id ( node-set ?)

Возвращает уникальный строковый идентификатор первого узла переданного множества или контекстного узла, если аргумент опущен.

object system-property ( string )

Возвращает значение свойства, имя которого передано как аргумент.

Обозначения

тип1 функция ( тип2 , тип3 , тип4 ?)

Прототип функции. Тип1 — тип возвращаемого значения, тип2, тип3, тип4 — типы передаваемых параметров.

Символ «?» обозначает аргумент, который может быть опущен.

Символ * для обозначения аргумента, который может повторяться несколько раз.

Функции xslt

В данной главе описываются специфичные для XSLT дополнения к основной библиотеке функций XPath. Некоторые из этих дополнительных функций задействуют также информацию, заданную в стиле элементами верхнего уровня. В главе описаны также и эти элементы.

12.1 Множественные исходные документы

Функция document позволяет получить доступ к иным XML-документам помимо основного исходного документа.

Если функция document имеет ровно один аргумент и этот аргумент является набором узлов, то результатом будет объединение результатов вызова функции document для каждого узла из набора, представленного как аргумент. При этом первым аргументом вызываемой функции будет строковое значение очередного узла, а вторым — набор узлов, в котором этот узел является единственным членом. Если же функция document имеет два аргумента, первый из которых являются набором узлов, то результатом будет объединение результатов вызова функции document для каждого узла из представленного в аргументе набора. При этом первым аргументом вызываемой функции будет строковое значение очередного узла, в качестве второго аргумента будет использован второй аргумент, переданный исходной функции document .

Если первый аргумент функции document не был набором узлов, он преобразуется в строку как при вызове функции string . Полученная строка обрабатывается как ссылка URI, то есть, извлекается ресурс, идентифицируемый этим URI. Полученные таким образом данные обрабатываются как XML документ и в соответствии с моделью данных (см. [ 3 Модель данных ]) строится дерево. Если при извлечении ресурса произошла ошибка, XSLT процессор может сигнализировать о ней. Если он этого не делает, то должен обработать ошибку сам, возвратив пустой набор узлов. Один из возможных типов ошибки извлечения ресурса связан с тем, что XSLT процессор не поддерживает схему, используемую данным URI. XSLT процессор не обязан поддерживать все возможные схемы URI. В документации для XSLT процессора должно быть указано, какие схемы URI поддерживаются этим процессором.

Если ссылка URI не содержит идентификатор фрагмента, возвращается набор узлов, содержащий только корневой узел документа. Если же ссылка URI содержит идентификатор фрагмента, то данная функция возвращает набор узлов из дерева, на которые указывает идентификатор фрагмента из этой ссылки URI. Семантика идентификатора фрагмента зависит от типа среды, полученного при извлечении URI. Если во время обработки идентификатора фрагмента возникла ошибка, XSLT процессор может сигнализировать об ошибке. Если он этого не делает, то должен обработать ошибку сам, вернув пустой набор узлов. Некоторые из возможных ошибок:


Идентификатор фрагмента определяет нечто, чего нельзя представить в виде набора узлов XSLT (например, диапазон символов в текстовом узле).

XSLT процессор не поддерживает идентификаторы фрагмента для типа среды, соответствующего полученному результату. XSLT процессор не обязан поддерживать все типы среды, которые могут существовать. В документации к XSLT процессору должно быть указано, для каких типов сред процессор поддерживает идентификаторы фрагментов.

Данные, полученные в результате извлечения, обрабатываются как XML документ независимо от типа среды у результата извлечения. Если типом среды верхнего уровня является text , то он обрабатывается точно так же, как если бы тип среды был text/xml . В противном случае обработка производится так, как если бы тип среды был application/xml .

Замечание: Поскольку для верхнего уровня нет типа среды xml , данные с иным типом среды, чем text/xml или application/xml действительно могут быть XML.

Ссылка URI может быть относительной. Из набора узлов, представленного во втором аргументе, берется узел, который первым встретится в документе, и его базовый URI (см. [ 3.2 Базовый URI ]) используется для преобразования относительных URI в абсолютные. Если второй аргумент опущен, то по умолчанию используется тот узел из стиля, который содержит выражение, включающее рассматриваемый вызов функции document . Заметим, что ссылка URI нулевой длины является ссылкой на документ, относительно которого эта ссылка URI разрешается. Таким образом, document(«») ссылается на корневой узел данного стиля, а представление стиля в виде дерева в точности такое же как если бы в качестве исходного документа был взят XML документ, изначально содержащий этот стиль.

Если два документа идентифицируются одним и тем же URI, то они обрабатываются как один и тот же документ. Для сравнения используется абсолютный URI, к каковому приводится любой относительный URI, не содержащий каких-либо идентификаторов фрагментов. Два корневых узла обрабатываются как один и тот же узел, если оба взяты из одного и того же документа. Таким образом, следующее выражение всегда будет давать true:

Функция document дает возможность держать в одном наборе узлов узлы сразу из нескольких документов. Для такого набора узлов относительный порядок следования двух узлов, взятых из одного и того же документа — это обычный порядок следования в документе, заданный в XPath [XPath]. Относительный порядок следования двух узлов из различных документов определяется тем, как реализовано упорядочение документов, содержащих эти узлы. Помимо согласованности (для одного и того же набора документов реализация всегда должна давать один и тот же порядок следования) каких-либо иных ограничений на реализацию упорядочения документов нет.

12.2 Ключи

Ключи дают возможность работать с документами, имеющими неявную структуру перекрестных ссылок. В XML атрибуты типа ID , IDREF и IDREFS создают механизм, позволяющий делать в документах XML перекрестные ссылки явными. В XSLT этот механизм реализуется с помощью функции id из XPath. Однако этот механизм имеет ряд ограничений:


Атрибуты ID должны быть декларированы в качестве таковых в DTD. Если атрибут ID декларирован в качестве такового лишь во внешнем наборе DTD, то и распознаваться как ID атрибут он будет только тогда, когда процессор XML прочтет этот внешний набор DTD. Однако спецификация XML вовсе не обязует XML процессоры читать этот внешний DTD, а потому процессоры вполне могут этого не делать, особенно если для документа было декларировано standalone=»yes» .


Документ может иметь только один набор уникальных ID. Не может быть несколько независимых наборов уникальных атрибутов ID.

ID элемента может быть задано лишь в атрибуте. Он не может быть задан ни содержимым элемента, ни элементом, являющимся непосредственным потомком.

ID должен быть именем XML. Например, он не может содержать пробелов.

Элемент может содержать не более одного ID.

Любой конкретный ID может принадлежать только одному элементу.

Из-за этих ограничений документы XML иногда имеют структуру перекрестных ссылок, которая не была явно декларирована атрибутами ID/IDREF/IDREFS.

Ключ определяется тремя параметрами:


узлом, которому принадлежит этот ключ

значением ключа (строка)

Набор ключей для каждого документа декларируется в стиле с помощью элемента xsl:key . Если в данном наборе ключей есть член с узлом x , названием y и значением z , то мы говорим, что узел x имеет ключ с названием y и значением z .

Таким образом, ключ — это такой тип обобщенного ID, на который не распространяются ограничения, принятые для ID в XML:


Ключи декларируются в стиле с помощью элементов xsl:key .

Ключи имеют как название, так и значение. Каждое название ключа можно рассматривать как отдельное, независимое пространство идентификаторов.

Для элемента значение именованного ключа можно указать в любом удобном месте, например, в содержимом самого элемента, в атрибуте или элементе, являющемся непосредственным потомком. Место, где следует искать значение конкретного именованного ключа, задается выражением XPath.

Значением ключа может быть произвольная строка и это не обязательно должно быть имя.

В документе может быть несколько ключей, относящихся к одному и тому же узлу и имеющих одно и то же название, но с различными значениями ключа.

В документе может быть несколько ключей, имеющих одно и то же название ключа, одно и то же значение, но относящихся к различным узлам.

name = qname
match = pattern
use = expression />

Чтобы декларировать ключи, используется элемент xsl:key . Название создаваемого ключа задает атрибут name . Значением атрибута name является QName, которое приводится к расширенному имени как было описано в главе [ 2.4 Полные имена ]. Атрибут match является шаблоном. Элемент xsl:key дает информацию о ключах во всех узлах, которые соответствуют шаблону, заданному атрибутом match. Атрибут use дает выражение, определяющее значение ключа. Для каждого узла, соответствующего шаблону, это выражение вычисляется отдельно. Если результатом вычисления является набор узлов, то для каждого узла в этом наборе считается, что узлы, соответствующие представленному шаблону, имеют ключ с указанным именем, значением которого является строковое значение данного узла из набора. В противном случае, результат вычисления преобразуется в строку, и соответствующий шаблону узел получает ключ с указанным названием и значением, соответствующим этой строке. Таким образом, узел x имеет ключ с названием y и значением z тогда и только тогда, когда имеется элемент xsl:key , такой что:


x соответствует шаблону, указанному в атрибуте match данного элемента xsl:key ,

атрибут name элемента xsl:key имеет значение, равное y , и

если выражение, заданное атрибутом use элемента xsl:key , обрабатывается с текущим узлом x , а в качестве текущего набора узлов используется список, состоящий из одного x , и при этом результатом является объект u , то либо z равно результату преобразования объекта u в строку (как при вызове функции string ), либо u — это набор узлов, а z равно строковому значению одного или нескольких узлов из набора u .

Заметим также, что одному узлу может соответствовать несколько элементов xsl:key . В этом случае будут использованы все совпавшие элементы xsl:key , даже если они имеют разный приоритет импорта.

Если значение атрибутов use или match содержит VariableReference, фиксируется ошибка.

Функция id играет для ключей ту же роль, что и функция key для идентификаторов ID. Первый аргумент функции указывает имя ключа. Значением данного аргумента должно быть QName, которое приводится к расширеному имени как было описано в главе [ 2.4 Полные имена ]. Если вторым аргументом функции key является список узлов, то результатом вызова будет объединение результатов вызова функции key для строкового значения каждого узла в списке, указанном в аргументе. Если же второй аргумент функции key имеет какой-либо иной тип, то этот аргумент преобразуется в строку, как при вызове функции string . При этом функция возвращает набор узлов из того же документа, где находится узел контекста, значение именованного ключа которых соответствует этой строке.

Например, дана декларация

выражение key(«idkey»,@ref) возвратит тот же набор узлов, что и id(@ref) , при условии, что в исходном XML-документе был декларирован единственный атрибут ID:

а атрибут ref текущего узла не содержит пробельных символов.

Предположим, что документ, описывающий библиотеку функций, для определения каковых используется элемент prototype

а чтобы обратиться к названию функции используется элемент function

В таком случае представленный стиль может генерировать гиперссылки между указанными ссылками и определениями следующим образом:

Функция key может использоваться для извлечения ключа из других документов, нежели тот, в котором содержится текущий узел контекста. Предположим, к примеру, что документ содержит библиографические ссылки в формате XSLT , а также есть отдельный XML-документ bib.xml , содержащий библиографическую базу данных с записями в формате:

В этом случае в стиле можно использовать следующий вариант преобразования элементов bibref :

12.3 Форматирование чисел

Функция format-number преобразует свой первый аргумент в строку, используя строку шаблона форматирования, представленную во втором аргументе, и десятичный формат, поименованый в третьем аргументе, либо десятичный формат по умолчанию, если третий аргумент отсутствует. Строка с шаблоном форматирования имеет синтаксис, определенный в JDK 1.1 для класса DecimalFormat. Строка шаблона форматирования представлена в локализованной нотации: десятичный формат определяет, какие именно символы в шаблоне имеют специальное значение (за исключением символа кавычки, который не подлежит локализации). Шаблон формата не должен содержать символ денежной единицы (#x00A4), такая возможность была добавлена уже после первой реализации JDK 1.1. Названием десятичного формата должно быть QName, которое приводится к расширенному имени как описано в главе [ 2.4 Полные имена ]. Если в стиле отсутствует декларация десятичного формата с заданным расширенным именем, фиксируется ошибка.

Замечание: Разработчики не обязаны использовать именно реализацию JDK 1.1, а сам анализатор не обязательно реализовывать на Java.

Замечание: Чтобы контролировать округление чисел, в стилях могут использоваться другие функции из XPath.

qname
decimal-separator = char
grouping-separator = char
infinity = string
minus-sign = char
NaN = string
percent = char
per-mille = char
zero-digit = char
digit = char
pattern-separator = char />

Элемент xsl:decimal-format декларирует десятичный формат, который управляет интерпретацией шаблона формата, используемого в функции format-number . Если присутствует атрибут name , данный элемент декларирует именованный десятичный формат. В остальных случаях декларируется десятичный формат по умолчанию. Значением атрибута name является QName, которое приводится к расширенному имени как было описано в главе [ 2.4 Полные имена ]. Если десятичный формат по умолчанию или десятичный формат с данным именем, декларируется несколько раз, фиксируется ошибка (даже при различном приоритете импорта). Это можно делать только если каждый раз для всех атрибутов декларированы одни и те же значения (принимая во внимание все значения по умолчанию).

Остальные атрибуты xsl:decimal-format соответствуют методам класса DecimalFormatSymbols из JDK 1.1. Для каждой пары методов get / set в элементе xsl:decimal-format определен соответствующий атрибут.

Следующие атрибуты задают интерпретацию символов в шаблоне формата, а также указывают символы, которые могут появиться в результате форматирования числа:


decimal-separator задает символ, используемый как десятичная точка, по умолчанию используется символ точки ( . )

grouping-separator задает символ, используемый как разделитель при группировке (например, тысяч), по умолчанию используется символ запятой ( , )

percent задает символ, используемый как символ процента, по умолчанию используется символ процента ( % )

per-mille задает символ, используемый как символ промилле, по умолчанию используется символ промилле из набора Unicode (#x2030)

zero-digit задает символ, используемый как цифра нуль, по умолчанию используется цифра нуль ( 0 )

Следующие атрибуты задают интерпретацию символов в шаблоне формата:


digit задает символ, используемый в шаблоне формата для обозначения цифр, по умолчанию таковым является символ решетки ( # )

pattern-separator задает символ, используемый для разделения положительной и отрицательной частей в шаблоне, по умолчанию используется символ точки с запятой ( ; )

Следующие атрибуты определяют символы или строки, которые могут появиться в результате форматирования числа:


infinity задает строку, используемую для обозначения бесконечности, по умолчанию используется строка Infinity

NaN задает строку, используемую для представления значения NaN, по умолчанию используется строка NaN

minus-sign задает символ, используемый по умолчанию как знак «минус», по умолчанию используется символ тире-минус ( — , #x2D)

12.4 Различные дополнительные функции

Функция current возвращает набор узлов, состоящий из единственного члена — текущего узла. Для внешнего выражения (выражения, которое не является частью другого выражения), текущий узел всегда тот же самый, что и узел контекста. Таким образом,

означает то же самое, что и

Однако внутри квадратных скобок текущий узел обычно отличается от узла контекста. Например,

будет обрабатывать все элементы item , которые имеют родителем элемент glossary , а также имеют атрибут name , значение которого равно значению атрибута ref текущего узла. Этот вариант отличается от

который означает то же самое, что

а потому будет обрабатывать все элементы item , которые имеют родителем элемент glossary , а также имеют атрибуты name и ref с одинаковыми значениями.

Если в шаблоне использовать функцию current , фиксируется ошибка.

unparsed-entity-uri возвращает URI неразобранной сущности с заданным именем, находящейся в том же документе, что и узел контекста (см. [ 3.3 Неразобранные сущности ]). Если такой сущности нет, функция возвращает пустую строку.

Илон Маск рекомендует:  Что такое код hw_insdoc

Функция generate-id возвращает строку, уникальным образом идентифицирующую тот узел из набора узлов, представленного в аргументе, который первым встретится в документе. Уникальный идентификатор должен состоять из алфавитно-цифровых символов ASCII и должен начинаться с буквы. Таким образом, данная строка синтаксическим соответствует имени XML. Разработчик волен генерировать идентификатор любым удобным ему способом при условии, что для одного и того же узла всегда генерируется один и тот же идентификатор, а для разных узлов всегда генерируются разные идентификаторы. Процессор не обязан генерировать одни и те же идентификаторы при каждом преобразовании документа. Нет гарантий, что сгенерированный уникальный идентификатор не совпадет с каким-либо уникальным ID, указанным у исходном документе. Если в аргументе был дан пустой набор узлов, функция возвращает пустую строку. Если аргумент отсутствует, то по умолчанию используется узел контекста.

Аргумент функции должен обрабатываться как строка QName. QName приводится к расширенному имени с помощью деклараций пространства имен в области видимости данного выражения. Функция system-property возвращает объект, представляющий значение системного параметра, идентифицируемого этим именем. Если такого системного свойства нет, должна возвращаться пустая строка.

Реализации процессора должны представлять следующие системные свойства, относящиеся к пространству имен XSLT:

  • xsl:version , число, указывающее версию XSLT, реализуемую данным процессором. Для XSLT процессоров, реализующих версию XSLT, описываемую данным документом, таким числом является 1.0
  • xsl:vendor , строка, идентифицирующая разработчика XSLT процессора
  • xsl:vendor-url , строка, содержащая URL, идентифицирующий разработчика данного XSLT процессора. Как правило, это страница хоста (домашняя страница) Web-сайта разработчика.

Учебник по XSLT

XSL («расширяемый язык таблиц стилей» от англ. eXtensible Stylesheet Language) — язык преобразования и визуализации для XML.

XSLT означает преобразование или трансформация XSL (от англ. XSL Transformations) — язык преобразования XML документов.

В данном учебнике рассказывается, как можно использовать XSLT для трансформации XML документов в другие форматы (например, для трансформации XML в HTML).

Для изучения материалов данного учебника по XSLT необходимо обладать базовыми знаниями в области:

XSLT первый шаг

1. Введение

Не прошло и трёх лет с тех пор, как у меня зародилась мысль о том, что пора изучать XSLT -))). Мысль зародилась, а везде ещё стоял PHP 4 и зверствовал Salbotron , который, мягко говоря, не отличался высокой производительностью. Да и редко какой браузер мог похвастаться поддержкой этого самого XSLT. По этим соображениям изучение столь перспективного направления я отложил до лучших времён. На данный момент можно смело заявить, что эти времена настали, поскольку вышел PHP 5 с поддержкой XSLT и сносной объектной моделью, а все топовые браузеры уже сами уверенно держат преобразования, только подавай XML. :)

Важные ссылки по теме, первоисточники:

  • http://w3c.org — комитет по разработке и продвижению стандартов всемирной паутины Internet. На данный момент он является первоисточником практически всех веб-ориентированных стандартов и рекомендаций.
  • http://www.w3.org/TR/xml — спецификация расширяемого языка разметки XML, который является основой современного веба. На момент написания статьи доступна пятая редакция версии 1.0, а также вторая редакция версии 1.1.
  • http://www.w3.org/TR/xml-names — спецификация использования пространств имён в XML.
  • http://www.w3.org/TR/xpath — спецификация по использованию языка поиска частей XML-документа XPath.
  • http://www.w3.org/TR/xsl/ — спецификация расширенного языка стилей XSL.
  • http://www.w3.org/TR/xslt — спецификация языка преобразований XSLT.
  • http://validator.w3.org/ — валидатор HTML.
  • http://www.w3.org/TR/xhtml1/ — спецификация XHTML1.0.

Переводы на русский язык:

Для лучшего понимания всего происходящего я рекомендую читать спецификации в следующем порядке:

  1. XML (это основа!)
  2. пространства имён (механизм разнородного XML-кода в одном файле)
  3. XPath (язык выборки элементов из дерева структуры)
  4. XSLT (преобразования)
  5. XHTML (то, к чему нужно стремиться)


Особо пытливые могут также уделить внимание расширенному языку стилей XSL.

2. Валидный XHTML

Что такое валидный XHTML? В первую очередь, это XML-документ, который должен соответствовать спецификации XML. Во-вторую, почти обычная HTML-страница, к которой все привыкли.

Почему нужен именно XHTML? Исключительно из соображений совместимости и кросс-браузерности. Страница в XHTML будет с большей вероятностью отображаться корректно в популярных браузерах, чем обычный HTML.

Для рядового клепателя страниц словосочетание XML-документ должно означать следующее:

  1. Документ содержит объявление XML-документа в самом начале страницы:
  2. Документ содержит один корневой элемент, в котором находятся все остальные.
  3. Все элементы (тэги) должны иметь закрывающую часть (
    ,

).

  • Атрибуты всегда имеют значение, которое обязательно указывается в кавычках (одинарных или двойных). Например, «radio» disabled= «disabled» /> .
  • Управляющие символы & , и > всегда должны маскироваться. Например, «?a=1&b=2» > & . Исключение составляет только , внутри которого спецсимволы можно не маскировать.
  • Также сам XHTML обязывает выполнять следующие условия:

    1. Документ должен объявлять пространство имён, в рамках которого будут использоваться элементы HTML.
    2. Документ должен объявлять DOCTYPE перед корневым элементом и указывать в нём один из типов XHTML и соответствующий DTD.

    Пример простого документа XHTML1.0:

    И так обо всём по порядку.

    Объявление XML-документа, в котором указывается его версия и кодировка.

    Для большей безопасности кодировку нужно всегда выставлять, иначе могут возникнуть проблемы с невалидными (по отношению к дефолтной кодировке) символами.

    Объявление типа документа и его схемы.

    Для XHTML 1.0 есть три типа — Strict (строгое соответствие рекомендациям W3C), Transitional (переходный тип) и Frameset (использование фреймов). Для каждого из них предусмотрен отдельный DTD.

    Объявление пространства имён и используемого языка.

    Очень важно указывать ссылку именно в таком регистре и никак иначе. Это связано с тем, что в XML имена элементов и содержимое их атрибутов регистрозависимы.

    Три версии XHTML1.0 предназначены для лучшей обратной совместимости:

    • Strict — обеспечивает наибольшее соответствие рекомендациям W3C со стороны браузеров. Однако и сам HTML-код должен следовать этим рекомендациям.
    • Transitional — менее строгое соответствие, которое заставляет браузер вести себя так, как если бы это был обычный HTML-документ.
    • Frameset — позволяет использовать фреймы.

    Помимо XHTML1.0 на данный момент доступен XHTML1.1:

    XHTML1.1 по сути является тем же XHTML1.0 Strict и призван вытеснить другие версии XHTML1.0. Однако, по сравнению с XHTML1.0 Strict, у него есть ряд отличий:

    1. Удалён атрибут lang , его роль выполняет xml:lang . (Модуль [ XHTMLMOD ])
    2. Для элементов a и map вместо атрибута name нужно использовать атрибут id . (Модуль [ XHTMLMOD ])
    3. Доступен набор элементов ruby . (Модуль [ RUBY ])

    Итак, если вам нужна наибольшая кросс-браузерность и совместимость с рекомендациями W3C, то XHTML1.1 самое оно!

    Из этих соображений результатом моих преобразований будет именно XHTML1.1.

    3. XSLT-преобразования

    Что такое XSLT? Это язык преобразований XML-документа, который был разработан как часть расширенного языка стилей (XSL).

    Зачем нужен XSLT? Он позволяет реализовать схему, при которой данные хранятся отдельно, а их представление отдельно. То есть, один XML-документ преобразуется с помощью другого XML-документа (XSL, в котором находятся XSLT-шаблоны) в конечный документ. Результатом может быть XML, HTML или текстовый документ любого формата.

    Для того, чтобы воспользоваться XSLT-преобразованиями, в первую очередь нужно сформировать правильный стиль XSL и подключить его к XML-файлу.

    Валидным XSL-документом является XML-документ, у которого задано пространство имён xsl и присутствует корневой элемент stylesheet. В самом простом случае стиль может выглядеть, например, так:

    Этот стиль не содержит каких-либо явных определений шаблонов или других элементов XSL. Однако, его уже можно использовать. Чтобы посмотреть результат, достаточно сформировать произвольный XML-документ и подключить к нему этот стиль:

    За подключение стиля отвечает строка:

    Если файлы text.xml и test.xsl созданы и находятся в одной папке, то с помощью любого XSLT-парсера можно преобразовать исходный test.xml в результирующий документ. В качестве парсера могут выступать все популярные браузеры (IE5+, FF2+, Opera9+ и другие), а также модули в языках программирования, например, в PHP. Если вы используете браузер, то достаточно открыть test.xml, и он сразу отобразит примерно такой результат:

    При этом кодировка результата будет UTF-8, несмотря на то, что исходный документ был сформирован в windows-1251. К сожалению, браузеры обычно не позволяют просмотреть код результирующего документа, но модуль XSLT в PHP5 даёт возможность передать результирующий код в переменную, которую можно сохранить в файл. Поэтому, используя PHP, я приведу исходный код результирующего документа:

    Этот код не является валидным XML-документом и тем более XHTML1.1. Для того, чтобы сформировать нужный код, я усложню исходный XSL-стиль и добавлю туда необходимые шаблоны и преобразования. При этом исходный XML-документ останется без изменений.

    В качестве примера я приведу XSL-стиль, который при помощи XSLT будет выводить список атрибутов исходного XML-документа с их значениями, при этом будет формироваться валидный XHTML1.1. Итак, стиль:

    Чтобы понять, как он работает, я распишу каждое действие отдельно:

    Документ сформирован в кодировке windows-1251, о чём сообщается в атрибуте encoding. Версию XML-документа желательно всегда указывать, это рекомендация W3C.

    Затем идёт объявление корневого элемента, стиля:

    Обязательным атрибутом является определение пространства имён xsl через атрибут xmlns:xsl= «http://www.w3.org/1999/XSL/Transform» .

    Следующим шагом в корневом элементе stylesheet объявляется, каким образом нужно формировать результирующий документ:

    • method= «xml» — метод вывода документа. Результирующий документ будет в формате XML.
    • encoding= «windows-1251» — кодировка результирующего документа.
    • omit-xml-declaration= «no» — пропускать или нет начальное объявление XML-документа ( ). Может иметь значение «yes» или «no» (актуально только для html).
    • indent= «yes» — формировать отступы согласно уровню вложенности. Может иметь значение «yes» или «no».
    • media-type= «text/xml» — MIME-тип результирующего документа (используется только для метода вывода html).
    • doctype-public= «-//W3C//DTD XHTML 1.1//EN» — тип результируюшего документа (DOCTYPE)
    • doctype-system= «http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd» — ссылка на DTD

    Если метод вывода объявлен html, то значения атрибутов encoding и media-type будут подставлены в заголовок страницы ( . ) посредством метатега.

    Объявление основного шаблона:

    Именно этот XSLT-шаблон соответствует корню исходного дерева и будет вызван первым для преобразования. Атрибут match принимает значения, которые должны соответствовать языку поиска элементов XPath.

    Остальные шаблоны, если таковые имеются, должны подключаться из этого шаблона при помощи средств XSLT.

    Формирование XHTML-страницы. Оно начинается с элемента , у которого указано пространство имён xhtml:

    Атрибут xmlns= «http://www.w3.org/1999/xhtml» указывает на пространство имён xhtml, которое будет применено по умолчанию к этому элементу и всем дочерним элементам, у которых оно не задано явно.

    Атрибут xml:lang= «ru» указывает на язык, в котором сформирована страница (будущая).

    Эта часть стиля была нужна для формирования атрибутики валидного XHTML1.1 кода.

    Теперь что касается XSLT-преобразований:

    Вставка простого текста:

    Текст «Мой список:» будет подставлен в тег

    Организация цикла по выборке:

    Атрибут select принимает выражение XPath, на основе которого делает выборку. Если выборка вернула список узлов, то начинает работать цикл по каждому элементу.

    В данном случае выборка вернёт список атрибутов для этого (корневого) и всех дочерних элементов.

    В данном случае проверяется на чётность позиция элемента в списке выборки. Если тест возвращает true (порядковый номер элемента чётный), то срабатывает содержимое этого элемента.

    Управление атрибутами вышестоящего элемента:

    В данном случае, если позиция элемента чётная (определяется вышестоящим if), то в стиль элемента
    будет прописан серый цвет фона.

    Вывод значений элемента:

    Этот код подставит в вышестоящий элемент строку, собранную из имени текущего элемента и его значения. Содержимое атрибута select соответствует XPath.

    Вывод ссылки на разработчика парсера XSLT:

    Этот небольшой код XSLT формирует ссылку на разработчика парсера XSLT. Во многих случаях она будет разная и содержать разные значения.

    Результатом обработки этого стиля ( test.xsl ) станет такой код:

    Этот код соответствует стандарту XHTML1.1 и был сформирован на основе исходного XML-документа. Для проверки можно воспользоваться валидатором от W3C, который расположен по адресу http://validator.w3.org/.

    В браузере этот код выглядит примерно так:

    IE 6 FireFox 3 Opera 9.02

    4. Приложение

    Ссылки на исходный код

    Постоянный адрес статьи //anton-pribora.ru/articles/xml/xslt-first-step/. /Автор: Прибора Антон Николаевич, 2009 год/

    Использование PHP5 для обработки XSLT

    Для получения результирующего документа при помощи PHP5 я использовал такой код:

    Дополнительную информацию по использованию XSLT в PHP5 можно найти по адресу http://ru2.php.net/manual/ru/book.xslt.php.

    Мысли вслух

    «Товарищи, мы стоим на краю огромной пропасти! И я предлагаю сделать большой, решительный шаг вперёд!»

    © 2020 Антон Прибора. При копировании материалов с сайта, пожалуйста, указывайте ссылку на источник.

    Прочитайте онлайн Технология XSLT | Другие дополнительные функции XSLT

    Другие дополнительные функции XSLT

    Выражение для этой функции имеет вид:

    node-set current()

    Функция current возвращает множество, состоящее из текущего узла преобразования.

    Мы часто использовали термины текущий узел и узел контекста как синонимы: действительно, в большинстве случаев между ними нет никакой разницы, текущий узел преобразования совпадает с узлом контекста вычисления выражений. Однако бывают ситуации, когда они являются двумя различными узлами.

    Представим себе, что нам нужно выбрать элементы item со значением атрибута source , равным значению этого атрибута текущего узла. Очевидно, путь выборки будет выглядеть как item[ предикат ] , где предикат определяет условие равенства атрибутов текущего и выбираемого. Но как записать это условие? Предикат будет вычисляться в контексте проверяемого элемента item , значит, все относительные пути выборки типа @source или ./@source или self::item/@source будут отсчитываться именно от проверяемого элемента. В этом случае узел контекста и текущий узел преобразования — не одно и то же.

    Для того чтобы обратиться в предикате именно к текущему узлу, следует использовать функцию current :

    Это выражение выберет все дочерние элементы item текущего узла, значение атрибута source которых будет таким же, как и у него.

    Выражение для этой функции следующее:

    string unparsed-entity-uri( string )

    Функция unparsed-entity-uri возвращает уникальный идентификатор ресурса, который соответствует неразбираемой внешней сущности, имя которой передано как аргумент.

    Описывая синтаксис XML, мы приводили пример документа, который использовал неразбираемые внешние сущности.

    Листинг 8.67 . Входящий документ использующий неразбираемые внешние сущности

    image ENTITY #REQUIRED

    h2 CDATA #REQUIRED

    href CDATA #REQUIRED>


    Для того чтобы вычислить местоположение графических файлов, соответствующих пунктам этого меню, нужно будет использовать функцию unparsed- entity-uri . Аргументом этой функции в данном случае будет значение атрибута image , ведь именно этот атрибут задает имя неразбираемой сущности, которая соответствует изображению пункта меню. Преобразование такого документа в HTML будет иметь приблизительно следующий вид.

    Листинг 8.68. Преобразование, использующее функцию unparsed-entity-uri

    Результат преобразования приведен на следующем листинге.

    Листинг 8.69. Выходящий документ

    Остается только добавить, что unparsed-entity-uri — это единственная функция, которая позволяет работать с неразбираемыми сущностями. Никаких средств для обработки нотаций и вспомогательных приложений, которые им соответствуют, в XSLT нет. Сказывается тот факт, что неразбираемые сущности и нотации очень редко используются в документах, поэтому их поддержка в XSLT минимальна.

    Синтаксическая конструкция этой функции:

    string generate-id( node-set? )

    Функция generate-id возвращает уникальный строковый идентификатор первого в порядке просмотра документа узла, передаваемого ей в виде аргумента. Если аргумент опущен, функция возвращает уникальный идентификатор контекстного узла. Если аргументом является пустое множество, функция должна возвращать пустую строку.

    Функция generate-id обладает следующими свойствами.

    □ Функция generate-id возвращает для двух узлов один и тот же идентификатор тогда и только тогда, когда эти два узла совпадают. Это означает, что во время выполнения одного преобразования функция generate-id будет возвращать один идентификатор для одного и того же узла, а для разных узлов generate-id обязательно возвратит разные идентификаторы.

    □ Возвращаемый идентификатор состоит только из цифр и букв ASCII и начинается буквой, то есть синтаксически является корректным XML-именем и может использоваться как имя элемента, атрибута, как значение ID-атрибута или в любом другом месте, где могут использоваться имена XML.

    Кроме этого спецификация оговаривает следующие важные положения, которые мы приведем ниже.

    □ Процессор не обязан генерировать один и тот же идентификатор при разных выполнениях преобразования одного и того же документа. Иными словами, если в понедельник процессор X при выполнении преобразования Y сгенерирует для узла Z документа D идентификатор I , то во вторник тот же процессор X при выполнении того же преобразования Y с тем же документом D может сгенерировать для того же самого узла Z совершенно другой, отличный от I идентификатор.

    □ Форма возвращаемого идентификатора может быть произвольной, но при этом она должна �иф�ежезворать аписанному вшже синтаксикт. Это означает, что�вадный процессоѰ может�п-� свЉем�сгенерировать идентификатот. Ёпецификация не определяет никакого нтнадЀстног�име� оаь раблиЂаций функцив generate-id может совпадитьра может�и нт совпадитe> со значентямт уникальнях атрибутой, то естх атрибутой, Ѹпв даннях которых�оЊ� явден в лрое DTDь как generate-id совершеЂо нмои� данном образмe> обеоЇщает задаче гфѿапирокми.�хоЀ> онщее ои этом м�врЁескожма��m[ I Пример

    item уникальнѼх атрибутом Листинг 7068. Преобразованиri

    XSLT, XPath, and XQuery Functions

    XSLT 2.0, XPath 2.0, and XQuery 1.0, share the same functions library.

    Functions Reference

    The default prefix for the function namespace is fn:
    The URI of the function namespace is: http://www.w3.org/2005/xpath-functions

    Tip: Functions are often called with the fn: prefix, such as fn:string(). However, since fn: is the default prefix of the namespace, the function names do not need to be prefixed when called.

    Accessor Functions

    Name Description
    fn:node-name(node) Returns the node-name of the argument node
    fn:nilled(node) Returns a Boolean value indicating whether the argument node is nilled
    fn:data(item.item. ) Takes a sequence of items and returns a sequence of atomic values
    fn:base-uri()
    fn:base-uri(node)
    Returns the value of the base-uri property of the current or specified node
    fn:document-uri(node) Returns the value of the document-uri property for the specified node

    Error and Trace Functions

    Name Description
    fn:error()
    fn:error(error)
    fn:error(error,description)
    fn:error(error,description,error-object)
    Example: error(fn:QName(‘http://example.com/test’, ‘err:toohigh’), ‘Error: Price is too high’)

    Result: Returns http://example.com/test#toohigh and the string «Error: Price is too high» to the external processing environment

    fn:trace(value,label) Used to debug queries

    Functions on Numeric Values

    Name Description
    fn:number(arg) Returns the numeric value of the argument. The argument could be a boolean, string, or node-set

    Example: number(‘100’)
    Result: 100

    fn:abs(num) Returns the absolute value of the argument

    Example: abs(3.14)
    Result: 3.14

    Example: abs(-3.14)
    Result: 3.14

    fn:ceiling(num) Returns the smallest integer that is greater than the number argument

    Example: ceiling(3.14)
    Result: 4

    fn:floor(num) Returns the largest integer that is not greater than the number argument

    Example: floor(3.14)
    Result: 3

    fn:round(num) Rounds the number argument to the nearest integer

    Example: round(3.14)
    Result: 3

    fn:round-half-to-even() Example: round-half-to-even(0.5)
    Result: 0

    Example: round-half-to-even(1.5)
    Result: 2

    Example: round-half-to-even(2.5)
    Result: 2

    Functions on Strings

    Name Description
    fn:string(arg) Returns the string value of the argument. The argument could be a number, boolean, or node-set

    Example: string(314)
    Result: «314»

    fn:codepoints-to-string((int,int. )) Creates a string from a sequence of the Unicode Standard code points

    Example: codepoints-to-string((84, 104, 233, 114, 232, 115, 101))
    Result: ‘Thérèse’

    fn:string-to-codepoints(string) Returns the sequence of the Unicode standard code points from a string

    Example: string-to-codepoints(«Thérèse»)
    Result: (84, 104, 233, 114, 232, 115, 101)

    fn:codepoint-equal(comp1,comp2) Returns true if the value of comp1 is equal to the value of comp2, according to the Unicode code point collation (http://www.w3.org/2005/02/xpath-functions/collation/codepoint), otherwise it returns false fn:compare(comp1,comp2)
    fn:compare(comp1,comp2,collation) Returns -1 if comp1 is less than comp2, 0 if comp1 is equal to comp2, or 1 if comp1 is greater than comp2 (according to the rules of the collation that is used)

    Example: compare(‘ghi’, ‘ghi’)
    Result: 0

    fn:concat(string,string. ) Returns the concatenation of the strings

    Example: concat(‘XPath ‘,’is ‘,’FUN!’)
    Result: ‘XPath is FUN!’

    fn:string-join((string,string. ),sep) Returns a string created by concatenating the string arguments and using the sep argument as the separator

    Example: string-join((‘We’, ‘are’, ‘having’, ‘fun!’), ‘ ‘)
    Result: ‘ We are having fun! ‘

    Example: string-join((‘We’, ‘are’, ‘having’, ‘fun!’))
    Result: ‘Wearehavingfun!’

    Example:string-join((), ‘sep’)
    Result: »

    fn:substring(string,start,len)
    fn:substring(string,start) Returns the substring from the start position to the specified length. Index of the first character is 1. If length is omitted it returns the substring from the start position to the end

    Example: substring(‘Beatles’,1,4)
    Result: ‘Beat’

    Example: substring(‘Beatles’,2)
    Result: ‘eatles’

    fn:string-length(string)
    fn:string-length() Returns the length of the specified string. If there is no string argument it returns the length of the string value of the current node

    Example: string-length(‘Beatles’)
    Result: 7

    fn:normalize-space(string)
    fn:normalize-space() Removes leading and trailing spaces from the specified string, and replaces all internal sequences of white space with one and returns the result. If there is no string argument it does the same on the current node

    Example: normalize-space(‘ The XML ‘)
    Result: ‘The XML’

    fn:normalize-unicode() fn:upper-case(string) Converts the string argument to upper-case

    Example: upper-case(‘The XML’)
    Result: ‘THE XML’

    fn:lower-case(string) Converts the string argument to lower-case

    Example: lower-case(‘The XML’)
    Result: ‘the xml’

    fn:translate(string1,string2,string3) Converts string1 by replacing the characters in string2 with the characters in string3

    Example: translate(’12:30′,’30’,’45’)
    Result: ’12:45′

    Example: translate(’12:30′,’03’,’54’)
    Result: ’12:45′

    Example: translate(’12:30′,’0123′,’abcd’)
    Result: ‘bc:da’

    fn:escape-uri(stringURI,esc-res) Example: escape-uri(«http://example.com/test#car», true())
    Result: «http%3A%2F%2Fexample.com%2Ftest#car»

    Example: escape-uri(«http://example.com/test#car», false())
    Result: «http://example.com/test#car»

    Example: escape-uri («http://example.com/

    bébé», false())
    Result: «http://example.com/

    fn:contains(string1,string2) Returns true if string1 contains string2, otherwise it returns false

    Example: contains(‘XML’,’XM’)
    Result: true

    fn:starts-with(string1,string2) Returns true if string1 starts with string2, otherwise it returns false

    Example: starts-with(‘XML’,’X’)
    Result: true

    fn:ends-with(string1,string2) Returns true if string1 ends with string2, otherwise it returns false

    Example: ends-with(‘XML’,’X’)
    Result: false

    fn:substring-before(string1,string2) Returns the start of string1 before string2 occurs in it

    Example: substring-before(’12/10′,’/’)
    Result: ’12’

    fn:substring-after(string1,string2) Returns the remainder of string1 after string2 occurs in it

    Example: substring-after(’12/10′,’/’)
    Result: ’10’

    fn:matches(string,pattern) Returns true if the string argument matches the pattern, otherwise, it returns false

    Example: matches(«Merano», «ran»)
    Result: true

    fn:replace(string,pattern,replace) Returns a string that is created by replacing the given pattern with the replace argument

    Example: replace(«Bella Italia», «l», «*»)
    Result: ‘Be**a Ita*ia’

    Example: replace(«Bella Italia», «l», «»)
    Result: ‘Bea Itaia’

    fn:tokenize(string,pattern) Example: tokenize(«XPath is fun», «\s+»)
    Result: («XPath», «is», «fun»)

    Functions for anyURI

    Name Description
    fn:resolve-uri(relative,base)

    Functions on Boolean Values

    Name Description
    fn:boolean(arg) Returns a boolean value for a number, string, or node-set
    fn:not(arg) The argument is first reduced to a boolean value by applying the boolean() function. Returns true if the boolean value is false, and false if the boolean value is true

    Example: not(true())
    Result: false

    fn:true() Returns the boolean value true

    Example: true()
    Result: true

    fn:false() Returns the boolean value false

    Example: false()
    Result: false

    Functions on Durations, Dates and Times


    Component Extraction Functions on Durations, Dates and Times

    Name Description
    fn:dateTime(date,time) Converts the arguments to a date and a time
    fn:years-from-duration(datetimedur) Returns an integer that represents the years component in the canonical lexical representation of the value of the argument
    fn:months-from-duration(datetimedur) Returns an integer that represents the months component in the canonical lexical representation of the value of the argument
    fn:days-from-duration(datetimedur) Returns an integer that represents the days component in the canonical lexical representation of the value of the argument
    fn:hours-from-duration(datetimedur) Returns an integer that represents the hours component in the canonical lexical representation of the value of the argument
    fn:minutes-from-duration(datetimedur) Returns an integer that represents the minutes component in the canonical lexical representation of the value of the argument
    fn:seconds-from-duration(datetimedur) Returns a decimal that represents the seconds component in the canonical lexical representation of the value of the argument
    fn:year-from-dateTime(datetime) Returns an integer that represents the year component in the localized value of the argument

    Example: year-from-dateTime(xs:dateTime(«2005-01-10T12:30-04:10»))
    Result: 2005

    fn:month-from-dateTime(datetime) Returns an integer that represents the month component in the localized value of the argument

    Example: month-from-dateTime(xs:dateTime(«2005-01-10T12:30-04:10»))
    Result: 01

    fn:day-from-dateTime(datetime) Returns an integer that represents the day component in the localized value of the argument

    Example: day-from-dateTime(xs:dateTime(«2005-01-10T12:30-04:10»))
    Result: 10

    fn:hours-from-dateTime(datetime) Returns an integer that represents the hours component in the localized value of the argument

    Example: hours-from-dateTime(xs:dateTime(«2005-01-10T12:30-04:10»))
    Result: 12

    fn:minutes-from-dateTime(datetime) Returns an integer that represents the minutes component in the localized value of the argument

    Example: minutes-from-dateTime(xs:dateTime(«2005-01-10T12:30-04:10»))
    Result: 30

    fn:seconds-from-dateTime(datetime) Returns a decimal that represents the seconds component in the localized value of the argument

    Example: seconds-from-dateTime(xs:dateTime(«2005-01-10T12:30:00-04:10»))
    Result: 0

    fn:timezone-from-dateTime(datetime) Returns the time zone component of the argument if any fn:year-from-date(date) Returns an integer that represents the year in the localized value of the argument

    Example: year-from-date(xs:date(«2005-04-23»))
    Result: 2005

    fn:month-from-date(date) Returns an integer that represents the month in the localized value of the argument

    Example: month-from-date(xs:date(«2005-04-23»))
    Result: 4

    fn:day-from-date(date) Returns an integer that represents the day in the localized value of the argument

    Example: day-from-date(xs:date(«2005-04-23»))
    Result: 23

    fn:timezone-from-date(date) Returns the time zone component of the argument if any fn:hours-from-time(time) Returns an integer that represents the hours component in the localized value of the argument

    Example: hours-from-time(xs:time(«10:22:00»))
    Result: 10

    fn:minutes-from-time(time) Returns an integer that represents the minutes component in the localized value of the argument

    Example: minutes-from-time(xs:time(«10:22:00»))
    Result: 22

    fn:seconds-from-time(time) Returns an integer that represents the seconds component in the localized value of the argument

    Example: seconds-from-time(xs:time(«10:22:00»))
    Result: 0

    fn:timezone-from-time(time) Returns the time zone component of the argument if any fn:adjust-dateTime-to-timezone(datetime,timezone) If the timezone argument is empty, it returns a dateTime without a timezone. Otherwise, it returns a dateTime with a timezone fn:adjust-date-to-timezone(date,timezone) If the timezone argument is empty, it returns a date without a timezone. Otherwise, it returns a date with a timezone fn:adjust-time-to-timezone(time,timezone) If the timezone argument is empty, it returns a time without a timezone. Otherwise, it returns a time with a timezone
    Name Description
    fn:QName()
    fn:local-name-from-QName()
    fn:namespace-uri-from-QName()
    fn:namespace-uri-for-prefix()
    fn:in-scope-prefixes()
    fn:resolve-QName()

    Functions on Nodes

    Name Description
    fn:name()
    fn:name(nodeset)
    Returns the name of the current node or the first node in the specified node set
    fn:local-name()
    fn:local-name(nodeset)
    Returns the name of the current node or the first node in the specified node set — without the namespace prefix
    fn:namespace-uri()
    fn:namespace-uri(nodeset)
    Returns the namespace URI of the current node or the first node in the specified node set
    fn:lang(lang) Returns true if the language of the current node matches the language of the specified language

    Example: Lang(«en») is true for

    Example: Lang(«de») is false for

    fn:root()
    fn:root(node) Returns the root of the tree to which the current node or the specified belongs. This will usually be a document node

    Functions on Sequences

    General Functions on Sequences

    Name Description
    fn:index-of((item,item. ),searchitem) Returns the positions within the sequence of items that are equal to the searchitem argument

    Example: index-of ((15, 40, 25, 40, 10), 40)
    Result: (2, 4)

    Example: index-of ((«a», «dog», «and», «a», «duck»), «a»)
    Result (1, 4)

    Example: index-of ((15, 40, 25, 40, 10), 18)
    Result: ()

    fn:remove((item,item. ),position) Returns a new sequence constructed from the value of the item arguments — with the item specified by the position argument removed

    Example: remove((«ab», «cd», «ef»), 0)
    Result: («ab», «cd», «ef»)

    Example: remove((«ab», «cd», «ef»), 1)
    Result: («cd», «ef»)

    Example: remove((«ab», «cd», «ef»), 4)
    Result: («ab», «cd», «ef»)

    fn:empty(item,item. ) Returns true if the value of the arguments IS an empty sequence, otherwise it returns false

    Example: empty(remove((«ab», «cd»), 1))
    Result: false

    fn:exists(item,item. ) Returns true if the value of the arguments IS NOT an empty sequence, otherwise it returns false

    Example: exists(remove((«ab»), 1))
    Result: false

    fn:distinct-values((item,item. ),collation) Returns only distinct (different) values

    Example: distinct-values((1, 2, 3, 1, 2))
    Result: (1, 2, 3)

    fn:insert-before((item,item. ),pos,inserts) Returns a new sequence constructed from the value of the item arguments — with the value of the inserts argument inserted in the position specified by the pos argument

    Example: insert-before((«ab», «cd»), 0, «gh»)
    Result: («gh», «ab», «cd»)

    Example: insert-before((«ab», «cd»), 1, «gh»)
    Result: («gh», «ab», «cd»)

    Example: insert-before((«ab», «cd»), 2, «gh»)
    Result: («ab», «gh», «cd»)

    Example: insert-before((«ab», «cd»), 5, «gh»)
    Result: («ab», «cd», «gh»)

    fn:reverse((item,item. )) Returns the reversed order of the items specified

    Example: reverse((«ab», «cd», «ef»))
    Result: («ef», «cd», «ab»)

    Example: reverse((«ab»))
    Result: («ab»)

    fn:subsequence((item,item. ),start,len) Returns a sequence of items from the position specified by the start argument and continuing for the number of items specified by the len argument. The first item is located at position 1

    Example: subsequence(($item1, $item2, $item3. ), 3)
    Result: ($item3, . )

    Example: subsequence(($item1, $item2, $item3, . ), 2, 2)
    Result: ($item2, $item3)

    fn:unordered((item,item. )) Returns the items in an implementation dependent order

    Functions That Test the Cardinality of Sequences

    Name Description
    fn:zero-or-one(item,item. ) Returns the argument if it contains zero or one items, otherwise it raises an error
    fn:one-or-more(item,item. ) Returns the argument if it contains one or more items, otherwise it raises an error
    fn:exactly-one(item,item. ) Returns the argument if it contains exactly one item, otherwise it raises an error

    Equals, Union, Intersection and Except

    Name Description
    fn:deep-equal(param1,param2,collation) Returns true if param1 and param2 are deep-equal to each other, otherwise it returns false
    Name Description
    fn:count((item,item. )) Returns the count of nodes
    fn:avg((arg,arg. )) Returns the average of the argument values

    Example: avg((1,2,3))
    Result: 2

    fn:max((arg,arg. )) Returns the argument that is greater than the others

    Example: max((1,2,3))
    Result: 3

    Example: max((‘a’, ‘k’))
    Result: ‘k’

    fn:min((arg,arg. )) Returns the argument that is less than the others

    Example: min((1,2,3))
    Result: 1

    Example: min((‘a’, ‘k’))
    Result: ‘a’

    fn:sum(arg,arg. ) Returns the sum of the numeric value of each node in the specified node-set

    Functions that Generate Sequences

    Name Description
    fn:id((string,string. ),node) Returns a sequence of element nodes that have an ID value equal to the value of one or more of the values specified in the string argument
    fn:idref((string,string. ),node) Returns a sequence of element or attribute nodes that have an IDREF value equal to the value of one or more of the values specified in the string argument
    fn:doc(URI)
    fn:doc-available(URI) Returns true if the doc() function returns a document node, otherwise it returns false
    fn:collection()
    fn:collection(string)

    Context Functions

    Name Description
    fn:position() Returns the index position of the node that is currently being processed

    XSLT Functions

    In addition, there are the following built-in XSLT functions:

    Введение в XSLT

    Преобразование формата XML-данных посредством преобразований расширяемого языка таблиц стилей (XSLT)

    Перед началом работы

    Данное руководство создано для разработчиков, которые хотят использовать XSLT для преобразования XML-данных в другие формы без необходимости программирования на Java™ или других языках.

    В данном руководстве подразумевается, что вы знакомы с XML, однако автор не вдается в какие-то тайные особенности этого языка, поэтому базового понимания вполне достаточно. За исключением небольшого раздела про расширения, вам не нужно иметь за плечами никакого особенного языка программирования. И даже в этом разделе идеи очень просты и применимы к любому языку программирования, который поддерживается процессором.

    О чем это руководство?

    Это руководство — о преобразованиях расширяемого языка таблиц стилей (XSLT). XSLT — это одна из базовых спецификаций, связанных с XML, позволяющая легко переводить XML-данные из одной формы в другую.

    В данном руководстве вы изучите:

    • Основы XSLT
    • Использование простых шаблонов
    • Публикацию данных
    • Управление пробелами
    • Основы XPath
    • Функции XPath
    • Циклы и условные операторы
    • Импорт и включение других таблиц стилей
    • Расширение XSLT
    • Переменные XSLT

    Начало работы

    Представьте на минуту, что у вас есть набор данных в XML. Возможно, вы предпочли хранить их таким образом из-за гибкости XML. Вы знаете, что сможете использовать их на разных платформах и практически с любым языком программирования. Но иногда вам придется менять форму вашего XML. Например, вам может понадобиться перевести данные в другой XML-формат, чтобы хранить их в другой системе, или в другую форму для представления, или еще для чего-нибудь.

    Например, у вас могут быть данные в XML, которые вы хотите опубликовать в Интернете, что означает перевод их в HTML. Конечно, вы могли бы вручную просмотреть документ и сделать в нем необходимые изменения, или может быть, вы подумали о том, чтобы загрузить XML в DOM, а затем вручную создать документ вывода.

    Что такое XSLT?

    Преобразования расширяемого языка таблиц стилей (XSLT) предоставляют способ для автоматического перевода XML-данных из одной формы в другую. Целевая форма — это обычно другой XML-документ, но не обязательно; вы можете преобразовать XML практически во что угодно, просто создав таблицу стилей XSLT и обработав данные. Если вы хотите изменить результаты, вы просто меняете таблицу стилей и обрабатываете XML заново. Здесь есть дополнительное преимущество, расширяющее возможности непрограммистов, например, дизайнеров, которые могут изменять таблицу стилей и влиять на результаты.

    Давайте посмотрим пример.

    Поставленная задача

    В данном руководстве мы возьмем XML-документ и преобразуем его в XHTML-документ, который можно отобразить как Web-страницу. Входные данные — это просто файл с рецептами (см. листинг 1).

    Листинг 1. Основные данные

    Замечание редактора: Эти рецепты –приведены просто для примера, так, как их представляет себе автор. Правильный рецепт для Gush’gosh (полученный от его жены, которая и готовит это блюдо) состоит из 1 фунта (0,454 кг) рубленой говядины, 1 фунта рожков, 1/2 стакана желтого сахара, 1 небольшого пакета (около 300 грамм) мелко нарезанного лука, 1 чайной ложки сушеного укропа и 1 небольшой банки томатной пасты, в которую добавляется желтый сахар.

    Конечно, это очень простой пример, поэтому вы не увязнете в деталях самих данных, однако в виде XML-данных можно представить что угодно — от процесса журналирования до финансов.


    Наша цель — преобразовать эти данные в XHTML-страницу, которая будет отображать рецепты по отдельности и форматировать их ингредиенты и инструкции по приготовлению (см. листинг 2).

    Листинг 2. Результат

    Вы можете отобразить этот результат в браузере, как показано на рисунке 1.

    Рисунок 1. Результат, отображенный в браузере

    Как уже упоминалось, конечной целью может быть любой формат, не только XHTML, и даже не обязательно XML.

    Давайте начнем с простых преобразований.

    Простая таблица стилей

    Самая простая таблица стилей — это просто XML документ, включающий XSLT-вывод (см. листинг 3).

    Листинг 3. Самая простая таблица стилей

    Обратите внимание на использование пространства имен xsl . Добавление этого пространства имен говорит процессору, какие элементы связаны с обработкой, а какие должны быть просто выведены. Элементы value-of говорят процессору вставить определенные данные в это место. Какие именно данные вставлять определяется содержимым атрибута select .

    Атрибут select состоит из выражения XPath. Подробнее XPath будет обсуждаться в разделе Подробнее об XPath, однако здесь вы можете видеть, что доступ к элементам «название», «ингредиенты» и «инструкция по приготовлению» происходит через иерархию документа. Мы начинаем с корневого элемента, /recipes , и от него движемся вниз.

    Как выполнить преобразование

    Простейший способ выполнить XML-преобразование — это добавить указание на таблицу стилей в XML и отобразить его в браузере (см. листинг 4).

    Листинг 4. Добавление в XML инструкции по обработке при помощи таблицы стилей

    Эта инструкция по обработке говорит браузеру извлечь таблицу стилей, расположенную в basicstylesheet.xsl, и использовать ее для преобразования XML-данных и вывода результатов. Если вы откроете наш XML-документ в браузере Microsoft® Internet Explorer®, то увидите результат, похожий на рисунок 2.

    Рисунок 2. Извлечение таблицы стилей и преобразование XML-данных

    Однако это не совсем то, что мы хотели получить. Если вы выберете в браузере Вид>Просмотр HTML-кода, то увидите изначальный XML. Чтобы увидеть результат преобразования, необходимо произвести это преобразование и создать выходной файл. Это можно сделать через командную строку, используя Java-код со следующей командой (см. листинг 5):

    Листинг 5. Преобразование документа через командную строку

    Если вы получите исключение ClassNotFoundException , возможно, вам нужно загрузить Apache Xalan (см. «Получить продукты и технологии» в разделе Ресурсы) и добавить включенные в него JAR-файлы в путь к классам.

    Выполнив преобразование, показанное в листинге 5, вы увидите, что файл result.html содержит следующий код (см. листинг 6).

    Листинг 6. Результаты

    Я добавил несколько интервалов для удобства чтения, однако здесь следует отметить пару моментов. Во-первых, листинг 6 отображает информацию только для одного рецепта. Во-вторых, ингредиенты слеплены вместе без каких-либо пробелов. Это тоже не тот результат, который мы хотели получить. К счастью, можно создать более точные шаблоны для отображения данных именно в той форме, в какой вы хотите.

    Добавление шаблонов

    От преобразования нет никакой пользы, если оно не приводит данные именно к той форме, которая нам нужна. Чтобы это сделать, нужно уметь использовать шаблоны, чему и посвящен данный раздел.

    Создание шаблонов

    Большинство таблиц стилей не имеют такой простой формы, какую вы только что видели в предыдущем разделе. Вместо этого они разбиваются на группу шаблонов, каждый из которых применяется к определенному типу данных. Давайте переведем нашу таблицу стилей в эту форму (см. листинг 7).

    Листинг 7. Переделанная таблица стилей

    Здесь информация не изменилась, за исключением того, что процессор смотрит на таблицу стилей и начинает с шаблона, совпадающего с корневым элементом документа, как указано в атрибуте match . Затем он выводит этот шаблон, включая все значения, так же как и раньше. Если вы выполните сейчас преобразование, то должны увидеть точно такие же результаты, как и в листинге 6.

    Но это не то, что мы хотим. Мы хотим иметь возможность форматировать ингредиенты и инструкции. Для этого мы можем создать отдельные шаблоны для каждого из этих элементов и включить их в таблицу стилей (см. листинг 8).

    Листинг 8. Создание дополнительных шаблонов

    Обратите внимание, что вместо того, чтобы просто вывести элемент value-of , мы теперь указываем таблице стилей применять соответствующие шаблоны к элементам ingredients и instructions. Затем мы создали отдельные шаблоны для этих элементов, задав их в атрибуте match . Добравшись до элемента apply-templates , процессор выбирает все элементы ingredients в документе. Затем он ищет шаблон для ингредиентов, а, найдя его, выводит этот шаблон. То же самое он делает с элементом instructions . Результат должен быть похож на изображенный на рисунке 3.

    Рисунок 3. Применение соответствующих шаблонов к элементам ingredients и instructions

    Так, это уже немного ближе, хотя бы понятно, что здесь два рецепта, однако вы вряд ли захотите объединять ингредиенты и инструкции для всех рецептов. К счастью, эту проблему можно решить лучшей организацией шаблонов.

    Публикация шаблонов

    Чтобы лучше организовать данные, обратим внимание на то, как мы разделяем и публикуем шаблоны. XSLT позволяет обрабатывать информацию итеративным образом. Например, можно поделить информацию на отдельные рецепты, а затем отформатировать инструкции и ингредиенты (см. листинг 9).

    Листинг 9. Выделение рецептов

    В данном случае таблица стилей выводит основную страницу (HTML), а затем просматривает каждый рецепт, выводя название, ингредиенты и инструкции для каждого рецепта. Опять-таки XPath мы будем изучать в разделе Подробнее об XPath, но в данном случае элемент recipe становится контекстным узлом, поэтому атрибуты select относятся к этому узлу так же как файлы к конкретному каталогу в файловой системе. Результат должен быть похож на рисунок 4.

    Рисунок 4. Элемент recipe становится контекстным узлом

    Отлично, формат приблизился к желаемому, однако нам все еще нужно отобразить фактическую информацию. Для этого надо изменить шаблоны ingredients и instructions (см. листинг 10).

    Листинг 10. Работа над шаблонами ingredients и instructions

    Важный момент при публикации шаблонов: вы указали процессору применять подходящие шаблоны к элементу ingredients , однако у нас нет специального шаблона для этого элемента. Если вы примените шаблон к элементу, а самого шаблона не будет, то данные просто не появятся. Но это не наш случай.

    Наоборот, мы воспользовались тем, что когда указали процессору применять подходящие шаблоны к элементу ingredients , он ищет не только элемент ingredients , но и дочерние элементы элемента ingredients . Таким образом, он находит шаблон для ингредиентов, в котором вы выводите количество, единицы измерения, название продукта и разрыв строки.

    То же самое мы сделали для инструкций, когда отформатировали их как элементы списка. Заметьте, что мы создали фактически упорядоченный список в основном шаблоне для рецепта, а затем отправили элементы для индивидуальной обработки.

    Результат должен быть похож на рисунок 5.

    Рисунок 5. Создание упорядоченного списка в основном шаблоне рецепта

    Определенно формат приближается к желаемому. Однако если вы посмотрите на вывод, то увидите, что проблема с пробелами все еще существует (см. листинг 11).

    Листинг 11. Недоработанный вывод

    Добавление пробелов

    Почему это случилось, если мы включали пробелы в таблицу стилей? Должны они были появиться в выводе? Разумеется, необязательно. Есть способы указать таблице стилей оставлять пробелы — они будут изучены в разделе Организация циклов и импорт — однако в некоторых случаях проще явно добавить текст в вывод (см. листинг 12).

    Листинг 12. Добавление текста

    Тем самым мы позаботились о недостающих пробелах. Точно так же с помощью элемента text , можно добавить в шаблон любой произвольный текст. (Помните, что только текст, а не элементы типа разрыва страницы.) Результатом будет такой вывод, какой нам нужен (см. листинг 13).

    Листинг 13. Окончательный вывод

    Результат показан на рисунке 6.

    Рисунок 6. Проблема отсутствующих пробелов решена

    Далее вы узнаете, как добавлять на страницу определенную информацию с помощью XPath.

    Основы XPath

    Возможность преобразовывать данные в форму, в какой вы желаете их видеть, требует понимания языка маршрутов XML, или XPath, который позволяет контролировать, какие именно данные публикуются и/или отображаются. Данный раздел объясняет основные понятия XPath и показывает, как создавать простые выражения.

    Что такое XPath?

    Вы должны были заметить, что важной частью функционирования таблицы стилей является возможность выбрать конкретную часть документа. Например, если вы хотите отобразить инструкции, то нужно знать, как на них ссылаться. В XSLT мы ссылаемся на информацию, используя выражения XPath.

    Выражение XPath может выбрать единственный узел или набор узлов, либо вернуть единственное значение, основанное на одном или более узлов в документе. До сих пор мы имели дело с очень простыми выражениями XPath, которые выбирают узел или узлы, спускаясь вниз по иерархической структуре. XPath предоставляет несколько способов для задания групп узлов на основании отношений, таких как родитель-потомок или предок-потомок. В данном руководстве мы изучим все эти отношения, называемые осями.

    Вы также узнаете о некоторых более мощных функциях XPath и о предикатах — это в основном условные утверждения, которые можно добавлять в выражения. Например, в разделе Настройка контекста вы увидите, как выбирать все элементы recipe в документе; предикаты позволяют выбрать только конкретный элемент на основании конкретных критериев.

    Наконец, вы узнаете о еще более мощных функциях, которые позволяют использовать многие типы логики, которые используются в процедурных программах.

    Давайте начнем с изучения контекста выражения.

    Настройка контекста

    Первым шагом в понимании XPath является понимание того, что полученные результаты в большой степени определяются текущим контекстным узлом. Контекстный узел можно считать своеобразным знаком «ты находишься здесь», от которого можно двигаться в различных направлениях согласно выражению XPath. Например, рассмотрим простую таблицу стилей в листинге 14.

    Листинг 14. Демонстрация контекста

    Здесь есть новый элемент — copy-of . Там, где value-of выводит содержимое элемента, copy-of делает именно то, о чем говорит его название, и выводит фактический узел, на который ссылается атрибут select .

    В данном случае в атрибуте select содержится одно из простейших возможных выражений XPath. Одиночная точка (.) – это ссылка на данный контекстный узел, как в файловой системе, когда вы хотите сослаться на «текущую директорию, вне зависимости от того, что это за директория.» Поэтому если вы выполните это преобразование, то получите результат, как в листинге 15.

    Листинг 15. Простейшее преобразование

    Вы видите, что это просто повторение того же документа; это потому, что контекстный узел, как указано атрибутом шаблона match, — это корень документа, или /. Если вы измените контекстный узел, вывод изменится. Например, вы можете установить контекстный узел на первый рецепт (см. листинг 16).

    Листинг 16. Перемещение контекстного узла

    Теперь, запустив преобразование, вы увидите разницу (см. листинг 17).

    Листинг 17. Результаты

    Так как мы переместили контекстный узел, вывод изменился. Это важно, когда вы хотите выбрать узел относительно контекстного узла. Например, вы можете выбрать только заголовок рецепта (см. листинг 18).

    Листинг 18. Выбор только заголовка

    Здесь вы выбираете элемент name , расположенный на один уровень ниже, чем текущий контекстный узел, поэтому результат будет как в листинге 19.

    Листинг 19. Результаты

    Мы изучим задание узлов по номеру позже, а пока отметим, что мы можем переместить контекстный узел во второй рецепт (см. листинг 20).

    Листинг 20. Еще одно перемещение контекстного узла

    (Не обращайте внимания на еще один шаблон, он нужен, чтобы не появлялся текст другого рецепта.)

    Мы можем видеть, что результирующее преобразование показывает это изменение контекста (см. листинг 21).

    Листинг 21. Результаты

    Для чего нужны оси

    Теперь, когда мы знаем, откуда мы начинаем, было бы полезно узнать, куда можно двигаться. До сих пор мы использовали очень простые выражения XPath, похожие на иерархию в файловой системе, однако XPath предоставляет гораздо больше возможностей. Например, до сих пор вы выбирали только дочерние элементы, однако вы можете также искать родителей конкретного узла, а также его потомков или предков.

    Для начала давайте поговорим о системе обозначений. Мы использовали упрощенную, укороченную форму запроса потомков. В «длинной форме» выражения задаются как child::name вместо ./name

    В обоих случаях вы задаете любой узел, который является потомком контекстного узла, а также является элементом name . Вы также можете связать их вместе, как в предыдущем случае, используя /child::recipes/child::recipe вместо /recipes/recipe .

    Потомки

    Возможно, вы недоумеваете, зачем мучаться с длинной формой, если короткая настолько проще. Это не было бы нужно, если бы единственное, что вы могли бы делать — это выбор потомков. Однако эту нотацию также можно использовать для выбора других отношений. Например, выражение XPath descendant::instruction выбирает все элементы instruction, которые являются потомками контекстного узла, а не только дочерние элементы. Кроме того, можно комбинировать инструкции. Например, вы можете выбрать все инструкции второго рецепта: /recipes/recipe[2]/descendant::instruction .

    Одной из разновидностей оси потомков является ось потомок-или-сам-элемент, которая выбирает заданный узел из всех потомков, а также ищет в самом контекстном узле. Например, выражение descendant-or-self::instructions выбирает все узлы instruction в контекстном узле и ниже его. Обычным сокращением для этой оси является двойная косая черта, //. Это означает, что выражения: /recipes/recipe[2]//instructions и //instructions выбирают все инструкции для второго рецепта и все инструкции в документе, соответственно. Этот второй пример очень широко применяется, он удобен, когда вы хотите просто выбрать все элементы определенного типа в документе.

    Атрибуты

    Еще одна общая задача, с которой вы столкнетесь — это необходимость выбрать атрибут для конкретного элемента. Например, вы, возможно, захотите выбрать recipeId для конкретного рецепта. Выражение /recipes/recipe/attribute::recipeId выбирает атрибут recipeId для всех элементов recipe . Эта ось также имеет сокращенную форму: это же выражение можно записать так: /recipes/recipe/@recipeId .

    Родители

    Все, что мы видели до этого, переносило нас вниз по дереву иерархии, но также существует возможность пойти вверх, выбрав предка конкретного узла. Предположим, что контекстным узлом была одна из инструкций, а вы хотите вывести recipeId для текущего рецепта. Вы можете сделать это следующим образом: ./parent::node()/parent::node()/@recipeId .

    Это выражение начинается в текущем узле — instruction -, а затем двигается к предку этого узла — элементу instructions — а затем к предку ТОГО элемента — recipe -, а затем к соответствующему атрибуту. Возможно, вам лучше знакома сокращенная форма: ./../../@recipeId .

    Теперь давайте посмотрим, как задаются некоторые условия.

    Более продвинутые возможности XPath

    В большинстве случаев уже рассмотренных приемов достаточно. Однако нередко встречаются ситуации, требующие большей избирательности. Данный раздел показывает, как использовать предикаты для выбора узлов, основанных на конкретных критериях, и дает представление о некоторых функциях, встроенных в XPath.

    Использование предикатов

    Зачастую требуется не просто любой узел, а конкретный узел, выбранный на основе конкретных условий. Вы ранее уже видели пример, когда использовали выражение /recipes/recipe[2]//instructions . Это на самом деле сокращенная версия выражения /recipes/recipe[position() = 2]//instructions , которое означает, что процессор XPath должен пройти через каждый элемент recipes (конечно, такой элемент только один) и для каждого элемента recipes пройти через каждый элемент recipe. Для каждого элемента recipe проверить истинность выражения position() = 2 . (Другими словами, есть ли в списке этот второй рецепт?) Если это предложение, называемое предикатом, истинно, то процессор использует этот узел и продолжает, возвращая любые инструкции.

    С помощью предикатов можно сделать множество вещей. Например, можно вернуть только рецепты, в которых есть название: /recipes/recipe[name] . Это выражение просто проверяет, существует ли элемент name — потомок элемента recipe . Также можно поискать конкретные значения. Например, можно возвращать только элементы, которые называются «A balanced breakfast»: //recipe[name=»A balanced breakfast»] .

    Имейте в виду, что предикат просто говорит процессору, возвращать ли текущий узел, поэтому в данном случае возвращается элемент recipe, а не имя. С другой стороны, можно указать процессору возвратить только название первого рецепта при помощи любого и этих двух выражений (см. листинг 22).


    Листинг 22. Возвращение только названия первого рецепта

    В первом выражении сначала мы выбираем все элементы recipe , а затем возвращаем только тот, у которого есть атрибут элемента recipeId , равный 1. Найдя этот узел, мы двигаемся к его дочернему узлу, который называется name, и возвращаем его. Во втором выражении сначала отыскиваются все элементы name , а затем выбирается только тот, у чьего родителя имеется атрибут 1. В любом случае, вы получите один и тот же вывод (см. листинг 23).

    Листинг 23. Вывод

    Функции

    XPath также предоставляет ряд функций. Некоторые из них относятся собственно к узлам, — например, те, которые ищут конкретную позицию, — некоторые обрабатывают строки, некоторые , — такие как суммирование, — с числами, некоторые с булевскими значениями.

    Функции, связанные с узлами

    Функции, связанные с узлами, помогают, например, выбрать конкретный узел в зависимости от позиции. Например, вы можете специально запросить последний рецепт: //recipe[last()] . Данное выражение выбирает все элементы recipe , а затем возвращает только последний. Вы также можете использовать функции отдельно, вместо того чтобы использовать их как часть предиката. Например, вы можете специально запросить посчитать элементы recipe: count(//recipe) .

    Вы уже видели функцию position() и то, как она работает. Другие функции, связанные с узлами, включают id() , local-name() , namespace-uri() и name() .

    Строковые функции

    Большинство строковых функций предназначено для обработки строк, а не для их проверки, за исключением функции contains() . Функция contains() показывает, является ли данная строка частью большего целого. Это позволит, например, вернуть только узлы, содержащие определенные строки, такие как: //recipe[contains(name, ‘breakfast’)] .

    Это выражение возвращает элемент recipe, который содержит в своем элементе name строку «breakfast».

    Функция substring() позволяет выбрать конкретный диапазон символов из строки. Например, выражение: substring(//recipe[2]/name, 1, 5) возвращает A bal .

    Первый аргумент — это полная строка, второй — позиция первого символа, а третий — это длина диапазона.

    Среди прочих строковых функций имеются: concat() , substring-before() , substring-after() , starts-with() и string-length() .

    Числовые функции

    Числовые функции включают функцию number() , которая переводит значение в числовое, чтобы другие функции могли с ним работать. Числовые функции также включают: sum() , floor() , ceiling() и round() . Например, вы можете найти сумму всех значений recipeId при помощи выражения: sum(//recipe/@recipeId) .

    Конечно, особого повода проводить подобное вычисление нет, однако это просто числовое значение, приведенное для примера.

    Функция floor() находит наибольшее целое, которое меньше или равно данному значению, а функция ceiling() находит наименьшее целое, которое больше или равно данному значению. Функция round() работает традиционным образом — округляет (см. листинг 24).

    Листинг 24. Результаты числовых функций

    Булевы функции

    Булевы функции наиболее полезны при работе с условными выражениями, о которых будет рассказано в разделе Условная обработка. Возможно, наиболее полезной функцией является not() , которая может быть использована для определения того, что определенный узел не существует. Например, выражение //recipe[contains(name, ‘breakfast’)] возвращает каждый рецепт, содержащий в элементе name строку «breakfast». Но что, если вам нужны все рецепты, кроме завтрака? Можно использовать выражение: //recipe[not(contains(name, ‘breakfast’))] .

    Другие булевы функции включают true() и false() , которые возвращают константы, и boolean() , которая преобразует значение в булево, чтобы его можно было использовать в качестве проверочного.

    Организация циклов и импорт

    Рассмотрим еще два важных аспекта использования таблиц стилей XSLT: создание циклов и импортирование внешних таблиц стилей.

    Организация циклов

    При работе с XSLT необходимо привыкнуть к тому, что это функциональный язык, а не процедурный. Другими словами, обычно вы явно не контролируете способ, которым он выполняет данные ему инструкции. Однако существуют исключения. Например, существует возможность организовывать циклы и выполнять условные операции. Давайте начнем с циклов.

    В предшествующем примере мы использовали выражения XSLT, встроенные в шаблон публикации, чтобы применить стиль к конкретным элементам. В некоторых случаях это отлично работает. Однако в ситуации, когда имеются сложные XML файлы или сложные требования, иногда проще обратиться к информации явно (см. листинг 25).

    Листинг 25. Прямое применение стилей с использованием циклов

    Конструкция цикла очень похожа на структуру for-each , в честь которой она названа. Подобно тезке, каждый экземпляр цикла несет в себе следующее значение списка. В Java-программировании это могло бы быть следующее значение в массиве; здесь это следующий узел в совокупности узлов, возвращенных выражением XPath в атрибуте select . Это означает, что когда вы первый раз выполняете первый цикл, контекстным узлом является первый элемент ingredient . Это позволяет выбрать потомков этого элемента qty, unit и food и добавить их в документ так же, как это было сделано ранее при помощи шаблона. То же самое и с инструкциями, за исключением того, что они просто выводятся напрямую.

    Результаты идентичны тем, что получаются при публикации через шаблоны — почти. Каждый шаблон добавляется к документу как индивидуальная строка, но так как нам необходимо обрабатывать эту информацию как один шаблон, мы теряем множество пробелов, которые видели раньше (см. рисунок 7).

    Рисунок 7. Результаты

    Для некоторых XML-приложений это имеет значение, но здесь мы используем HTML, поэтому это значения не имеет. Однако помните об этом, когда решаете, какой метод использовать.

    Включение и импорт таблиц стилей

    Другая вариация таблиц стилей касается их структуры. До сих пор вся наша информация хранилась в единственной таблице стилей. Однако в некоторых случаях бывает полезно разбить ее на отдельные таблицы стилей. Такая модульность может облегчить поддержку, а также гибкость, позволяя использовать различные таблицы стилей для различных целей. Например, мы можем создать две различные таблицы стилей, одна из которых будет использоваться для ингредиентов (см. листинг 26).

    Листинг 26. Файл ingredients.xsl

    Также можно создать отдельную таблицу стилей для инструкций (см. листинг 27).

    Листинг 27. Файл instructions.xsl

    Эти шаблоны идентичны шаблонам из действующей таблицы стилей. Их можно добавить к таблице стилей посредством включения (см. листинг 28).

    Листинг 28. Включение таблиц стилей

    Хотя здесь мы разместили таблицы стилей в той же директории, что и основную таблицу, делать это не обязательно: атрибут href может содержать любой доступный URL. Обратите внимание, что мы отсылаем процессор на поиск шаблонов для элементов ingredients и instructions , которых нет в этом файле. Однако если обработать таблицу стилей, результат получится точно тот же, как если бы шаблоны были включены напрямую, а не через элемент include (см. рисунок 8).

    Рисунок 8. Результаты

    Элемент include дает тот же результат, как если бы мы добавили содержимое начиная с данной точки. Семантически они идентичны. С другой стороны, есть еще один способ включения информации — импорт.

    XSLT позволяет импортировать таблицу стилей в начало файла. Почему в начало? Потому что целью импорта таблицы стилей является возможность корректировки любых шаблонов, являющихся частью импорта. Например, мы можем импортировать шаблон ingredients и откорректировать его (см. листинг 29).

    Листинг 29. Импорт таблицы стилей

    Здесь мы фактически заменили один шаблон двумя, которые заменяют шаблоны в импортированной таблице стилей (см. рисунок 9).

    Рисунок 9. Результаты

    Обратите внимание, что, так как иерархия зависит от расположения, того же результата можно было бы добиться и с помощью включения. Однако XSLT позволяет использовать атрибут priority , чтобы задать приоритет шаблонов при обработке импорта.

    Расширение XSLT

    Мы увидели, как сделать XSLT более похожим на программирование. Как насчет добавления к нему фактического программирования? Давайте взглянем на добавление функциональности Java к таблице стилей XSLT.

    Во-первых, следует отметить, что хотя механизм расширения является частью рекомендаций XSLT, рассматриваемая здесь реализация специфична для процессора Xalan XSLT. Основные идеи практически одинаковы для других процессоров, однако для уточнения деталей вам придется сверяться с документацией.

    Далее мы создадим расширение, которое позволит нам масштабировать рецепт на несколько порций.

    Элементы расширения

    Расширение XSLT производится с помощью различных методик. Первая — это использование элементов extension . Элемент extension — это элемент в пространстве имен, который указывает на класс Java. Взгляните, например, на следующий элемент extension (см. листинг 30).

    Листинг 30. Использование элемента extension

    Мы создали пространство имен, которое соответствует классу comp.backstop.RecipeScaler , который включает в себя статический метод под названием scaleMessage (см. листинг 31).

    Листинг 31. Класс RecipeScaler

    Добравшись до элемента, процессор видит префикс пространства имен scaler: и знает, что он обозначен как префикс элемента extension, и таким образом понимает, какой класс обозначен в определении пространства имен. Вызываемый им метод отвечает локальному имени элемента — scaleMessage . Сам метод получает два аргумента, из которых мы фактически используем один. Параметр context ссылается на контекст процессора, который позволяет взглянуть на элементы, относящиеся к элементу extension , однако мы просто займемся самим элементом extension . Так как мы получаем этот элемент как параметр метода, мы можем извлечь значения любых атрибутов, добавленных к этому элементу, таких как servings в данном случае. Текст, возвращенный методом, добавляется к выводу на месте элемента extension .

    Это означает, что если применить таблицу стилей, мы получим результаты, показанные на рисунке 10.

    Рисунок 10. Результаты элемента extension

    Элементы extension могут быть весьма полезны, хотя и несколько сложны в использовании.

    Функции extension

    Еще один способ добавить функциональность при помощи таблиц стилей — это использование функций extension, которые несколько проще реализовывать, чем элементы extension. Например, мы можем создать функцию, которая умножает количество ингредиентов и количество порций (см. листинг 32).

    Листинг 32. Метод scaleIngredient()

    Добавление этой функции в таблицу стилей идентично добавлению элемента extension, в котором уже есть карта пространства имен для класса (см. листинг 33).

    Листинг 33. Добавление функции extension

    Учтите, что вызов функции включает префикс пространства имен. Как и ранее, процессор видит префикс и знает, что надо выполнить вызов класса RecipeScaler . В результате количество ингредиентов умножается на два (см. рисунок 11).

    Рисунок 11. Использование функции extension

    Хотя данный код и работает, поддерживать его сложно. Давайте посмотрим, как упростить поддержку.

    Программирование XSLT

    Прежде, чем закончить изложение, рассмотрим два аспекта XSLT, которые предоставляют некоторые возможности, характерные для обычных языков программирования.

    Переменные XSLT

    Прекрасно, что мы можем выполнить функцию, но дело кончилось константой, запрятанной в глубине таблицы стилей. Не лучше ли было бы задавать переменные в начале страницы? Конечно, да (см. листинг 34).

    Листинг 34. Задание переменной

    XSLT позволяет создавать переменные; в ссылке используется знак доллара ($), который можно увидеть в листинге. Если применить таблицу стилей, можно увидеть два эффекта (см. рисунок 12).

    Рисунок 12. Использование переменной

    Обратите внимание, что ингредиенты были умножены на количество порций, как и ожидалось. Однако если вы посмотрите внимательнее, то увидите, что элемент extension сработал неверно, приняв переменную за строку, вместо того чтобы использовать значение самой переменной. Это не сбой; спецификация не требует, чтобы процессор что-либо делал со значениями атрибутов перед обработкой элемента extension. Поэтому нам надо как-то избежать этой проблемы.

    Условная обработка

    Первое, что мы можем сделать — это использовать условную обработку, чтобы сообщение отображалось, только если оно нужно. Например, см. листинг 35.

    Листинг 35. Использование элемента if

    Содержимое элемента if , заданное атрибутом test, должно быть равно true (истина). Если это не так, что и произошло в данном случае, вывод не появится вовсе (см. рисунок 13).

    Рисунок 13. Результаты предложения if

    В том виде, в каком оно написано, предложение не имеет особого смысла; если значение больше единицы, элемент extention отобразится со значением «3.» Лучше использовать множественный выбор (см. листинг 36).

    Листинг 36. Элемент choose

    В данном случае у нас имеется комбинация элементов if-then-else и предложения case из традиционных языков программирования. Элемент choose работает как контейнер, однако элемент when отображает его содержимое, только если его атрибут test равен true (истина). Наконец, если ни один из элементов when не равен true (истина), процессор отображает содержимое элемента otherwise .

    Результат получается такой, какого следует ожидать (см. рисунок 14).

    Рисунок 14. Результат

    Теперь вы можете более гибко задать отображение большего количества порций, или подстроиться под другие условия.

    Заключение

    Подведение итогов

    Данное руководство позволило продвинуться от начального знакомства с преобразованиями XSLT до составления достаточно сложных таблиц стилей. Сначала вы познакомились с основами таблиц стилей, затем с выражениями XPath — одной из основ XSLT. В последней части данного руководства были рассмотрены некоторые более сложные аспекты таблиц стилей XSLT — переменные, условная обработка и расширения. В результате у вас теперь должно быть достаточно знаний, чтобы делать с таблицами стилей XSLT практически все необходимое — или хотя бы понимать, что еще надо выяснить, если столкнетесь с проблемой.

    CVI. Функции XSLT

    XSLT (Extensible Stylesheet Language (XSL) Transformations) это язык трансформации XML-документов в другие XML-документы. Это стандарт, определённый консорциумом World Wide Web (W3C). Информацию о XSLT и связанных технологиях можно найти на http://www.w3.org/TR/xslt.

    Это расширение использует Sablotron и expat, которые находятся на http://www.gingerall.com/. Имеются как экзешники, так и исходный код.

    В UNIX запустите configure с опциями —enable-xslt —with-xslt-sablot . Библиотека Sablotron должна быть установлена там, где ваш компилятор может её найти.

    Это расширение PHP предоставляет не зависящий от процессора API для трансформаций XSLT. В настоящее время это расширение поддерживает только библиотеку Sablotron от Ginger Alliance. Планируется поддержка и других библиотек, таких как библиотеки Xalan или libxslt.

    Создание пользовательской функции XSLT

    Я пытаюсь создать пользовательскую функцию XSLT, но каждый раз, когда я получаю эту ошибку:

    ‘Первый аргумент нестатической функции Java’ compareCI ‘не является допустимой ссылкой на объект.’

    Я надеюсь, что кто-то из вас может мне помочь. Спасибо заранее.

    Я думаю, вы пытаетесь запустить это с помощью Xalan, который является процессором XSLT 1.0 и поэтому не распознает функцию xsl:. Случается, что (а) Xalan игнорирует функцию xsl: поскольку процессор XSLT 1.0, которому дается таблица стилей, указывающая версию = «2.0», должен игнорировать то, что он не понимает (так называемый «режим совместимости с переходом» в спецификации ); а затем, когда он видит вызов функции foo: compareCI(), он считает, что это должен быть вызов внешнего метода Java.

    Вам нужно запустить это с помощью процессора XSLT 2.0, обычно Saxon.

    Илон Маск рекомендует:  Как вконтакте сделать много подписчиков
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL