Перейти к основному содержимому

3.04. Справочник по XSLT

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Справочник по XSLT

🔹 Часть 1. Структура XSLT-таблицы стилей и глобальные элементы

XSLT-таблица — это XML-документ (обычно с пространством имён http://www.w3.org/1999/XSL/Transform, сокращённо xsl:). Корневой элемент — <xsl:stylesheet> или <xsl:transform> (синонимы).

🔸 1.1 Корневые элементы

ЭлементОбязательные атрибутыНеобязательные атрибутыЗамечания
<xsl:stylesheet>versionid, extension-element-prefixes, exclude-result-prefixes, xpath-default-namespace, default-collation, default-validation, default-mode, expand-text, use-package, package-version, streamability, default-attribute-version, default-function-namespace, input-type-annotations, output-version, _xml:id и др.Основной корневой элемент. Поддерживает модульность (XSLT 3.0 — пакеты).
<xsl:transform>То жеТо жеСемантический синоним <xsl:stylesheet>.

Атрибуты корневого элемента (обязательные и ключевые необязательные)

АтрибутВозможные значенияПрименимостьПояснение
version"1.0", "2.0", "3.0"Обяз.Определяет версию XSLT. Должна соответствовать реализации (напр., xsl:stylesheet version="3.0" требует XSLT 3.0-процессора: Saxon-EE/PE/HE, MorganaXSLT и др.).
idxs:IDНеобяз.Идентификатор элемента для внешней ссылки (например, xml:id).
extension-element-prefixesсписок префиксов пространств имёнНеобяз.Префиксы, используемые для элементов-расширений (например, saxon:, exsl:). Процессор не будет пытаться интерпретировать эти элементы как XSLT.
exclude-result-prefixes"#all" или список префиксовНеобяз.Префиксы, которые не должны копироваться в результирующий XML (кроме случаев, когда они используются в результатах). #all исключает все, кроме xml, xsl, xmlns, xs, xsi, err и префиксов, объявленных в use-attribute-sets.
xpath-default-namespaceIRIXSLT 2.0+Значение по умолчанию для default element/type namespace в XPath-выражениях (аналогично xpath-default-namespace в XQuery). Полезно при работе с XML с пространством имён, но без префиксов.
default-collationIRIXSLT 2.0+URI коллации по умолчанию для xsl:sort, compare(), starts-with() и т.д. Стандартные: http://www.w3.org/2005/xpath-functions/collation/codepoint (по кодам Unicode), http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive.
default-validation"strip", "preserve", "strict", "lax"XSLT 2.0+Управляет обработкой xsi:schemaLocation и xsi:noNamespaceSchemaLocation входного XML. strip — игнорировать типы; preserve — сохранять типы, не проверяя; strict/lax — проверять против схемы (требуется XSD).
default-modexs:QNameXSLT 3.0+Имя режима, используемого по умолчанию, если в xsl:apply-templates не указан mode. Может быть #unnamed, #default, #current, #text-only, #deep-copy.
expand-text"yes", "no"XSLT 3.0+Разрешает или запрещает text value templates ({...} внутри текстового содержимого). По умолчанию no.
use-packageURI пакетаXSLT 3.0+Указывает пакеты, из которых импортируются функции, переменные, шаблоны через <xsl:use-package>.
package-versionверсия (строка)XSLT 3.0+Версия текущего пакета.
streamability"unclassified", "shallow-sibling", "deep-sibling", "shallow-descent", "deep-descent", "absorbing"XSLT 3.0+Подсказка для потоковой обработки (streaming). Требуется Saxon-EE.
default-attribute-version"1.0", "2.0", "3.0"XSLT 3.0+Версия атрибутов по умолчанию для xsl:attribute, xsl:element и др.
default-function-namespaceIRIXSLT 3.0+Пространство имён, в котором искать функции без префикса. По умолчанию http://www.w3.org/2005/xpath-functions.
input-type-annotations"strip", "preserve", "unspecified"XSLT 3.0+Управляет, сохраняются ли аннотации типа узлов входного документа.
output-version"1.0", "2.0", "3.0"XSLT 3.0+Версия XSLT, для которой оптимизирован вывод (влияет на сериализацию).

🔸 1.2 Глобальные объявления (дочерние <xsl:stylesheet>)

ЭлементКраткое назначениеКлючевые атрибуты
<xsl:import>Импорт другой таблицы стилей (в начале, до любых других объявлений). Упорядочен: импортированные стили имеют меньший приоритет, чем основная таблица.href (обяз.), use-when
<xsl:include>Включение содержимого другой таблицы стилей (как макроподстановка). Должен находиться после всех <xsl:import>.href, use-when
<xsl:namespace-alias>Сопоставление префиксов пространств имён (для генерации XSLT из XSLT).stylesheet-prefix, result-prefix
<xsl:preserve-space> / <xsl:strip-space>Управление обработкой whitespace-only текстовых узлов входного XML.elements (QName*, #all)
<xsl:output>Определение параметров сериализации результата.name, method, version, encoding, omit-xml-declaration, standalone, doctype-public, doctype-system, cdata-section-elements, indent, media-type, use-character-maps, suppress-indentation, html-version, json-node-output-method, build-tree, allow-duplicate-names, byte-order-mark, escape-uri-attributes, include-content-type, include-xml-declaration, item-separator, suppress-indentation (XSLT 3.0+)
<xsl:param>Глобальный параметр (доступен во всей таблице).name, select, as, tunnel, required, static
<xsl:variable>Глобальная переменная.name, select, as, static
<xsl:function>Определение пользовательской функции.name, as, override, visibility, override-extension-function, streamability, saxon:memo-function, saxon:lazy и др.
<xsl:attribute-set>Именованный набор атрибутов для <xsl:element> и др.name, use-attribute-sets
<xsl:key>Индекс по пути (для key() и xsl:key()).name, match, use, composite
<xsl:decimal-format>Формат для format-number().name, атрибуты форматирования (decimal-separator, grouping-separator, infinity, minus-sign, NaN, percent, per-mille, zero-digit, digit, pattern-separator)
<xsl:character-map>Замена символов при сериализации (например, ©&copy;).name, use-character-maps
<xsl:accumulator>Накопитель для потоковой обработки (XSLT 3.0).name, initial-value, as, streamable, initial-value, phase
<xsl:mode>Определение поведения режима (XSLT 3.0).name, streamable, on-no-match, on-multiple-match, warning-on-no-match, build-tree, typed
<xsl:expose>Экспорт компонентов из пакета (XSLT 3.0).component, names, visibility
<xsl:package>(Внешне) — корневой элемент пакета XSLT 3.0.name, package-version, declared-modes, visibility, default-validation, default-collation, xpath-default-namespace

🔸 1.3 Элемент <xsl:output> — детализация

АтрибутВозможные значенияПримечания
method"xml", "html", "xhtml", "text", "json" (XSLT 3.0+), "adaptive""adaptive": вывод зависит от типа результирующего узла (элемент → XML, текст → текст и т.д.)
version"1.0", "4.0", "5.0" (для HTML), "1.0" (для XML), "1.1" (редко)
encoding"UTF-8", "UTF-16", "windows-1251", "KOI8-R" и др.Кодировка результирующего потока.
omit-xml-declaration"yes", "no"Для method="xml" — подавлять <?xml version="1.0"?>.
standalone"yes", "no"Атрибут standalone в декларации XML.
doctype-public / doctype-system"..."Для DTD.
cdata-section-elementsсписок QNamesЭлементы, содержимое которых сериализуется в <![CDATA[...]]>.
indent"yes", "no"Форматирование отступами. Не гарантируется.
media-type"text/html", "application/xml", "application/json" и др.HTTP Content-Type.
use-character-mapsимена карт (через пробел)Применяются при сериализации.
suppress-indentationсписок QNames или "#all"Элементы, у которых отключается indent="yes" (XSLT 3.0).
html-version"5.0", "4.01"Версия HTML (для method="html").
json-node-output-method"xml", "json", "text"Для узлов типа map(*)/array(*) → как сериализовать в JSON.
build-tree"yes", "no"no — в потоковом режиме: узлы не строятся в памяти.
item-separatorстрокаРазделитель для method="text" при выводе последовательности.

⚠️ Важно: в XSLT 3.0 xsl:output может быть объявлен внутри <xsl:package>, <xsl:function>, <xsl:template> — локальный вывод.


🔹 Часть 2. Шаблоны (xsl:template), контекст выполнения, режимы и управление применением

🔸 2.1 Элемент <xsl:template>

Это базовая единица обработки в XSLT. Определяет правило сопоставления и тело преобразования.

АтрибутОбяз.Возможные значенияПримечания
match❌ (но обязателен, если нет name)XPath-шаблон (Pattern) — подмножество XPathСопоставляется с узлами входного дерева. См. ниже раздел «XPath Patterns».
name❌ (но обязателен, если нет match)xs:QNameИменованный шаблон — вызывается через <xsl:call-template>. Может совмещаться с match.
modexs:QName, #default, #unnamed, #current, #text-only, #deep-copy, #all (XSLT 3.0)Режим обработки. Если не указан — #default.
priorityxs:decimalЯвный приоритет шаблона при разрешении конфликтов. По умолчанию вычисляется автоматически (см. ниже).
asSequenceType (XSLT 2.0+)Тип возвращаемого значения (например, xs:string*, element()*, item()*). Проверяется при xsl:sequence/автовыводе.
visibility"public", "private", "final", "abstract", "hidden" (XSLT 3.0)Контроль доступа (пакеты). hidden — не наследуется и не переопределяется.
override"yes", "no" (XSLT 3.0, устаревшее)Заменяет шаблон из импортированной таблицы с тем же match/mode. В XSLT 3.0 предпочтительно использовать visibility.
streamable"yes", "no", "sliding", "absorbing" (XSLT 3.0)Поддержка потоковой обработки. Требует Saxon-EE.
use-whenXPath-выражение (вычисляется на этапе компиляции)Условная компиляция шаблона. Должно быть статическим выражением (без ссылок на узлы/параметры времени выполнения).

Вложенность и содержимое <xsl:template>

  • Может содержать любые XSLT-инструкции, текст, литеральные элементы результата.
  • Допустимы <xsl:param> (локальные параметры шаблона).
  • Начиная с XSLT 3.0: <xsl:context-item> — декларация типа контекстного элемента (as, use).

🔸 2.2 Контекст выполнения шаблона

При срабатывании шаблона устанавливается динамический контекст (dynamic context), который включает:

КомпонентОписаниеДоступ в XPath/XSLT
Контекстный узел (context node)Узел, для которого вызван шаблон (совпал с match)., self::node(), name(), local-name(), namespace-uri()
Контекстная позиция (context position)Позиция узла в текущей последовательности (например, 1-й из 5)position() — в xsl:for-each, xsl:apply-templates, xsl:for-each-group
Контекстный размер (context size)Общее число узлов в текущей последовательностиlast()
Контекстные переменныеПараметры (xsl:param), локальные переменные (xsl:variable), глобальные переменные/параметры$name
Текущий режим (current mode)Режим, в котором вызван шаблон (xsl:apply-templates/@mode)Не напрямую, но влияет на разрешение xsl:apply-templates без mode
Текущая группа (current group)При xsl:for-each-group — текущая группа узловcurrent-group(), current-grouping-key(), current-grouping-key()
Предшествующий и последующий узел (preceding/following)В потоковом режиме — ограниченный доступТолько через аккумуляторы (XSLT 3.0)

⚠️ Ограничения в потоковом режиме (XSLT 3.0 streaming):

  • Нельзя использовать //, preceding::, following::, ancestor::, position() > 1, last(), count(//*) и др.
  • Допустимо: child::, attribute::, self::, parent:: (если streamable="absorbing"), xsl:iterate, xsl:accumulator.

🔸 2.3 XPath Patterns (шаблонные выражения для match)

Элемент match принимает Pattern, а не произвольный XPath. Pattern — это подмножество XPath с ограничениями.

✅ Допустимые конструкции:

КонструкцияПримерПримечания
Тест имениpara, *:section, svg:rect, Q{http://example.com}fooПространства имён должны быть объявлены.
Тест типаtext(), comment(), processing-instruction(), node(), document-node(), element(), attribute()
Комбинированные тестыelement(book), attribute(id), document-node(element(bookstore))XSLT 2.0+
Комбинирование через ```chapter
С предикатамиpara[@lang='en'], section[heading], *[position() mod 2 = 1]Предикаты могут содержать любые XPath-выражения, кроме ссылок на контекст (кроме . и @*).
Осиchild::para, attribute::id, self::node()Только child, attribute, self, parent, ancestor, descendant, following-sibling, preceding-sibling. descendant-or-self::node() разрешён (для /).
Универсальные шаблоны/, *, @*, node()

Запрещено в Pattern:

  • //недопустимо. Используйте descendant-or-self::node() только в /.
  • position(), last() — только внутри предикатов, и то ограниченно.
  • Переменные ($var) — нельзя в Pattern (XSLT 3.0 позволяет только в <xsl:evaluate>).
  • Вызовы функций, зависящие от контекста (кроме name(), local-name(), namespace-uri() для текущего узла).
  • id(), key()нельзя в match (только в предикатах при вызове, но не в самом шаблоне).

✅ XSLT 3.0 позволяет использовать параметризованные шаблоны через <xsl:use-when>, но не через $var в match.


🔸 2.4 Приоритет шаблонов и разрешение конфликтов

Если несколько шаблонов подходят одному узлу — выбирается наиболее приоритетный.

Алгоритм:

  1. У всех подходящих шаблонов вычисляется priority:

    • Явно заданный через @priority (xs:decimal, напр. priority="2.5");
    • Иначе — автоматически:
      • *|/|@*|node()-0.5
      • ns:*-0.25
      • * для элемента без префикса → 0.0
      • name0.0
      • name[expr]0.5
      • ns:name[expr]0.5
      • node()[expr]0.5
      • / (корневой) → 0.5
      • id('x')недопустимо в match, но если бы — 0.5
      • //nameнедопустимо, но если бы — -0.5
  2. Из шаблонов с максимальным приоритетом:

    • Если ровно один — берётся он.
    • Если несколько — ошибка времени выполнения «ambiguous rule match», если только:
      • Все шаблоны находятся в одной таблице → ошибка.
      • Шаблоны в разных таблицах — побеждает шаблон из таблицы с большим приоритетом импорта (последняя импортированная имеет больший приоритет, чем импортировавшая её).

✅ XSLT 3.0 смягчает это через <xsl:mode on-multiple-match="use-last" | "use-first" | "fail">.


🔸 2.5 Режимы (mode)

Режим — механизм полиморфной обработки одного и того же узла по-разному.

Значение modeПояснение
не указан / #defaultСтандартный режим.
#unnamedТо же, что #default.
QName (например, toc, html:body)Пользовательский режим.
#current (XSLT 2.0+)Передаёт текущий режим вложенному xsl:apply-templates без указания mode.
#text-only (XSLT 3.0)Автоматический режим: копирует только текстовое содержимое (аналог xsl:apply-templates select="text()"/>).
#deep-copy (XSLT 3.0)Полное глубокое копирование (аналог copy-of).
#all (XSLT 3.0, в xsl:apply-templates)Применяет шаблоны во всех именованных режимах (редко используется).

Управление по умолчанию (XSLT 3.0, <xsl:mode>):

<xsl:mode name="toc" on-no-match="shallow-skip"/>
<xsl:mode name="#default" on-no-match="shallow-copy"/>
<xsl:mode name="strict" on-no-match="fail"/>

Возможные значения on-no-match:

  • shallow-copy — копировать элемент и атрибуты, но не детей (стандартное поведение XSLT 1.0).
  • deep-copy — полное копирование.
  • shallow-skip — пропустить (ничего не выводить).
  • text-only — только текст.
  • fail — ошибка.
  • use-next-match — применить следующий по приоритету шаблон (рекурсивно).

🔸 2.6 <xsl:apply-templates> — управление применением

АтрибутОбяз.ЗначенияПояснение
selectXPath-выражение → node()*Последовательность узлов для обработки. По умолчанию — дочерние узлы контекстного узла (child::node()).
modexs:QName, #default, #current, #text-only, #deep-copy и др.Режим. Если #current — наследует режим текущего шаблона.
tunnel"yes", "no"Передача туннельных параметров (см. ниже).
use-accumulators"yes", "no"Использовать аккумуляторы (XSLT 3.0 streaming).
saxon:threadsчислоПараллельная обработка (Saxon-PE/EE).
xsl:fallbackДочерний элемент для fallback-обработки при ошибке (редко).

Поведение по умолчанию:

<xsl:apply-templates/> 

<xsl:apply-templates select="child::node()" mode="#default"/>

Туннельные параметры (tunnel="yes"):

  • Параметр объявляется с tunnel="yes" в <xsl:with-param>.
  • Может быть принят любым шаблоном на пути, даже если он не объявлен как <xsl:param name="x" tunnel="yes"/>.
  • Если шаблон не объявляет параметр — он автоматически «протуннелируется» дальше.

Пример:

<xsl:apply-templates select="chapter">
<xsl:with-param name="lang" tunnel="yes" select="'en'"/>
</xsl:apply-templates>

<!-- в шаблоне для para (вложен в chapter) -->
<xsl:template match="para">
<xsl:param name="lang" tunnel="yes" as="xs:string"/>
<p class="{$lang}">{.}</p>
</xsl:template>

🔸 2.7 <xsl:call-template>

Вызов именованного шаблона (аналог подпрограммы).

АтрибутОбяз.Пояснение
namexs:QName шаблона с name="...".
tunnel"yes" — передаёт все туннельные параметры.
<xsl:with-param>Передача параметров (со select, as, tunnel).

❗ Контекстный узел не меняется — остаётся тот же, что и в вызывающем шаблоне.


🔹 Часть 3. Генерация результата: статические и динамические конструкции

🔸 3.1 Литеральные элементы результата

Любой элемент вне пространства имён xsl: (и не исключённый через exclude-result-prefixes) копируется «как есть» в выходной документ, с заменой:

  • {...}Attribute Value Templates (AVT): в значениях атрибутов — интерполируются как XPath-выражения (XSLT 1.0+).
  • { {...} } — в XSLT 3.0 при expand-text="yes"Text Value Templates (TVT): в текстовом содержимом элементов.

Пример (XSLT 1.0):

<a href="report-{$id}.html" title="Report for {name(.)}">
<xsl:value-of select="title"/>
</a>

Пример (XSLT 3.0, expand-text="yes"):

<xsl:stylesheet version="3.0" expand-text="yes">
<xsl:template match="book">
<div class="book" id="b-{isbn}">
Title: {title}, Price: {format-number(price, '¤#,##0.00')}
</div>
</xsl:template>
</xsl:stylesheet>

⚠️ AVT/TVT экранируются: {{{, }}}.


🔸 3.2 Динамическое создание узлов

ЭлементНазначениеОбяз. атрибутыКлючевые необяз. атрибуты
<xsl:element>Создаёт элемент с динамическим именемname (XPath, xs:QName)namespace, inherit-namespaces, use-attribute-sets, validation, type (XSLT 2.0+)
<xsl:attribute>Создаёт атрибутnamenamespace, select, separator, validation, type, use-when
<xsl:namespace>Создаёт узел пространства имён (редко)nameselect
<xsl:processing-instruction>PI-узелnameselect, use-when
<xsl:comment>Комментарийselect, use-when
<xsl:text>Текстовый узел (гарантированно)disable-output-escaping, use-when
<xsl:value-of>Вывод значения выраженияselect, separator, disable-output-escaping
<xsl:sequence>Вывод последовательности (XSLT 2.0+)select, use-when
<xsl:document>Создаёт фрагмент документа (XSLT 1.0) или временный документ-узел (XSLT 2.0+)validation, type, abort, use-when

Атрибут validation (XSLT 2.0+):

  • "strip" — игнорировать типы (по умолчанию),
  • "preserve" — сохранять типы из схемы,
  • "strict"/"lax" — валидировать против схемы (требуется XSD).

Атрибут type (XSLT 2.0+):

  • xs:QName типа (например, xs:integer, element(book)).

Атрибут inherit-namespaces (xsl:element и xsl:copy):

  • "yes" (по умолчанию) — копировать все объявления xmlns:* из текущего узла.
  • "no" — не наследовать (только пространства, используемые в имени/атрибутах).

🔸 3.3 Копирование узлов

ЭлементНазначениеАтрибуты
<xsl:copy>Поверхностное копирование узла (только сам узел и его пространства имён/атрибуты)copy-namespaces, inherit-namespaces, use-attribute-sets, validation, type
<xsl:copy-of>Глубокое копирование (всё поддерево)select, copy-accumulators, copy-namespaces, type, validation

Особенности:

  • <xsl:copy> — не копирует детей; обычно используется в шаблоне match="node()" с <xsl:apply-templates/>.
  • <xsl:copy-of> работает с любыми узлами, а также с map(*), array(*) (XSLT 3.0 → сериализует в JSON, если method="json").
  • copy-namespaces="no" — отключает копирование xmlns:*, кроме тех, что нужны для имён узлов/атрибутов.

🔸 3.4 Управление последовательностями и типами

ЭлементНазначениеПримечания
<xsl:sequence>Вывод последовательности (замена xsl:copy-of для нетипизированных данных)Рекомендуется вместо xsl:copy-of, когда копирование не требуется. Не создаёт узлов — передаёт значения напрямую.
<xsl:map> / <xsl:map-entry>(XSLT 3.0) Создание map(*)<xsl:map> — корневой; <xsl:map-entry key="..." select="..."/>
<xsl:array> / <xsl:array-member>(XSLT 3.0) Создание array(*)<xsl:array> — корневой; <xsl:array-member select="..."/> или вложенное содержимое
<xsl:merge>(XSLT 3.0) Объединение нескольких источников в потоковом режимеТребует streamable="yes"; работает с xsl:merge-source, xsl:merge-key — для join-подобных операций.

Пример map:

<xsl:variable name="config" as="map(xs:string, item()*)">
<xsl:map>
<xsl:map-entry key="'version'" select="3.0"/>
<xsl:map-entry key="'lang'" select="'en'"/>
<xsl:map-entry key="'features'">
<xsl:array>
<xsl:array-member select="'streaming'"/>
<xsl:array-member select="'packages'"/>
</xsl:array>
</xsl:map-entry>
</xsl:map>
</xsl:variable>

Доступ: $config?version, $config?features?2.


🔸 3.5 Сериализация: управление выходным форматом

XSLT 3.0 ввёл гибкую сериализацию, включая JSON.

Методы вывода (xsl:output/@method):

МетодОсобенностиПоддержка
xmlСтандартный XML (с декларацией, сущностями, и т.д.)XSLT 1.0+
htmlHTML-совместимый вывод (без закрывающих /, регистронезависимые имена, &nbsp; и др.)XSLT 1.0+
xhtmlXHTML (корректный XML с HTML-семантикой)XSLT 2.0+
textЧистый текст (без разметки; item-separator используется между элементами последовательности)XSLT 1.0+
jsonСериализация map(*), array(*), xs:string, xs:double, xs:boolean, null в JSONXSLT 3.0+
adaptiveАвтоопределение: элемент → XML, строка → текст, map/array → JSON и т.д.XSLT 3.0+

Параметры JSON-сериализации (через xsl:output):

АтрибутЗначенияПримечание
json-node-output-method"xml", "json", "text"Как выводить не-JSON-узлы внутри map/array.
allow-duplicate-names"yes", "no"Разрешить дубли ключей в map(*) → при no — ошибка.
escape-uri-attributes"yes", "no"Экранировать URI в атрибутах (для xml/xhtml).
include-content-type"yes", "no"Добавлять Content-Type: application/json в комментарий.
include-xml-declaration"yes", "no"Для method="xml"/xhtml.

Пример JSON-вывода:

<xsl:output method="json" indent="yes"/>

<xsl:template match="/">
<xsl:sequence select="
map {
'books': array {
//book ! map {
'title': string(title),
'price': number(price),
'authors': array { author ! string(.) }
}
}
}"/>
</xsl:template>

Результат:

{
"books": [
{
"title": "XSLT Guide",
"price": 29.99,
"authors": ["Timur"]
}
]
}

🔸 3.6 Управление экранированием: disable-output-escaping

ЭлементыАтрибутВозможные значения
<xsl:value-of>, <xsl:text>disable-output-escaping"yes", "no" (по умолчанию)
  • "yes" — отключает экранирование &, <, > → выводит «как есть».
  • Предупреждение: не поддерживается:
    • При method="text" (игнорируется),
    • При потоковой обработке (XSLT 3.0 streaming),
    • В некоторых процессорах (например, в браузерных XSLT 1.0 — может не работать).

🚫 Не рекомендуется для генерации XML/HTML — нарушает well-formedness. Использовать только для legacy-совместимости (например, вставки CDATA или pre-сериализованного HTML).


🔹 Часть 4. Управление потоком: условия, циклы, группировка, обработка исключений

🔸 4.1 Условные конструкции

ЭлементНазначениеОбяз. атрибутыЗамечания
<xsl:if>Простая условная ветвьtest (XPath-выражение)Эквивалент xsl:choose с одним xsl:when. Не имеет else.
<xsl:choose>Многоусловная конструкцияОбязательно содержит ≥1 <xsl:when>; опционально <xsl:otherwise>.
<xsl:when>Ветвь условия внутри xsl:choosetestВыполняется первая истинная ветвь.
<xsl:otherwise>Ветвь по умолчаниюДолжна быть последней в xsl:choose.

Пример:

<xsl:choose>
<xsl:when test="price &lt; 10">cheap</xsl:when>
<xsl:when test="price &lt; 50">moderate</xsl:when>
<xsl:otherwise>expensive</xsl:otherwise>
</xsl:choose>

⚠️ Оптимизация: XSLT-процессоры (например, Saxon) могут оптимизировать xsl:choose в switch-подобные конструкции, если условия — простые строковые или целочисленные сравнения.


🔸 4.2 Итерация: <xsl:for-each>

АтрибутОбяз.ЗначенияПояснение
selectXPath → item()*Последовательность для итерации.
sort (вложенный <xsl:sort>)Сортировка перед итерацией (см. ниже).
saxon:threadsxs:integerПараллельная обработка (Saxon PE/EE). Требует идемпотентности тела.

Контекст внутри <xsl:for-each>:

  • Контекстный узел — текущий элемент последовательности.
  • position() — позиция в текущей итерации (1…N).
  • last() — общее число итераций (count($seq)).

Пример сортировки:

<xsl:for-each select="book">
<xsl:sort select="author" order="ascending" lang="en"/>
<xsl:sort select="year" data-type="number" order="descending"/>
<li>{author}, {title} ({year})</li>
</xsl:for-each>
Атрибуты <xsl:sort>:
АтрибутВозможные значенияПримечания
selectXPathВыражение для извлечения ключа сортировки. По умолчанию — строковое значение текущего узла.
order"ascending", "descending"По умолчанию — "ascending".
data-type"text", "number", "qname""text" — лексикографически (по lang/collation), "number" — численно.
langxs:language (например, "ru", "en-US")Язык для локализованной сортировки (например, ё после е).
collationURI коллацииПерекрывает lang. Стандартные URI: http://www.w3.org/2005/xpath-functions/collation/codepoint, .../html-ascii-case-insensitive.
case-order"upper-first", "lower-first"Влияет при data-type="text".
stable"yes", "no" (XSLT 2.0+)Устойчивая сортировка (сохранение исходного порядка при равенстве ключей).

🔸 4.3 Группировка: <xsl:for-each-group>

Ключевой механизм XSLT 2.0+ для агрегации данных.

АтрибутОбяз.ЗначенияПояснение
selectitem()*Последовательность для группировки.
group-by✅ (или group-adjacent, group-starting-with, group-ending-with)XPathВыражение, возвращающее ключ группы (любой тип, но сравнимый).
collationURIКоллация для строковых ключей.

Типы группировки:

ТипАтрибутПоведение
По значениюgroup-byВсе узлы с одинаковым ключом → одна группа (аналог SQL GROUP BY).
По смежностиgroup-adjacentГруппа формируется из смежных узлов с одинаковым ключом.
По началуgroup-starting-withXPath-шаблон: новая группа начинается при совпадении.
По окончаниюgroup-ending-withXPath-шаблон: группа заканчивается при совпадении.

Контекстные функции внутри тела:

ФункцияВозвращаетПримечание
current-group()item()*Все элементы текущей группы.
current-grouping-key()xs:anyAtomicTypeКлюч текущей группы (для group-by/group-adjacent).
current-grouping-start()node()?Узел, начавший группу (group-starting-with).
current-grouping-end()node()?Узел, завершивший группу (group-ending-with).

Пример (группировка книг по автору):

<xsl:for-each-group select="book" group-by="author">
<h2>{current-grouping-key()}</h2>
<ul>
<xsl:for-each select="current-group()">
<li>{title} ({year})</li>
</xsl:for-each>
</ul>
</xsl:for-each-group>

⚠️ Производительность: group-by строит хеш-таблицу → O(n log n); group-adjacent — линейный проход → O(n). При потоковой обработке допустим только group-adjacent (и то с ограничениями).


🔸 4.4 Итерация с состоянием: <xsl:iterate> (XSLT 3.0)

Обобщённая итерация с передачей состояния (аналог fold).

АтрибутОбяз.ЗначенияПояснение
selectitem()*Исходная последовательность.
initial-valueXPathНачальное состояние (по умолчанию — пустая последовательность).

Вложенность:

  • <xsl:iterate> содержит:
    • <xsl:param name="x" as="..." select="..."/> — входное состояние,
    • Тело итерации (может содержать <xsl:on-completion>, <xsl:break>),
    • <xsl:on-completion> — результат после последней итерации (по умолчанию — значение последнего $x).

Пример (накопление суммы):

<xsl:variable name="total" as="xs:double">
<xsl:iterate select="item" initial-value="0.0">
<xsl:param name="sum" as="xs:double"/>
<xsl:next-iteration>
<xsl:with-param name="sum" select="$sum + number(price)"/>
</xsl:next-iteration>
<xsl:on-completion select="$sum"/>
</xsl:iterate>
</xsl:variable>

✅ Поддерживает потоковую обработку, если тело — потоковое.


🔸 4.5 Анализ строк: <xsl:analyze-string>

Разбор текста по регулярному выражению (XSLT 2.0+).

АтрибутОбяз.ЗначенияПояснение
selectxs:stringАнализируемая строка.
regexxs:stringРегулярное выражение (синтаксис XPath 2.0, почти как ICU).
flags"i", "m", "s", "x" и др.Флаги: i — case-insensitive, m^/$ по строкам, s. включает \n, x — игнорировать пробелы в regex.

Дочерние элементы:

ЭлементНазначение
<xsl:matching-substring>Обработка совпадающих фрагментов.
<xsl:non-matching-substring>Обработка несовпадающих фрагментов.
<xsl:fallback>Обработка ошибок (редко).

Контекст внутри:

  • . — текущий подстроковый фрагмент (xs:string),
  • regex-group(n) — n-я захваченная группа (n ≥ 0; 0 — всё совпадение),
  • regex-group() — все группы как xs:string*.

Пример (извлечение ссылок):

<xsl:analyze-string select="." regex="https?://[^\s]+">
<xsl:matching-substring>
<a href="{.}">{.}</a>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>

⚠️ Производительность: сложные regex с backtracking могут быть медленными. Избегайте .* без ограничений.


🔸 4.6 Обработка ошибок: <xsl:try> / <xsl:catch> (XSLT 3.0)

Аналог try/catch в императивных языках.

ЭлементНазначениеАтрибуты
<xsl:try>Блок, в котором могут возникнуть ошибки
<xsl:catch>Обработчик исключенияerrors (список QNames ошибок), select (для ошибки как узла)

Структура:

<xsl:try>
<!-- защищённый код -->
<xsl:sequence select="doc('missing.xml')"/>
<xsl:fallback>
<xsl:message>Fallback used</xsl:message>
</xsl:fallback>
<xsl:catch errors="err:FODC0002">
<xsl:message>Error loading document</xsl:message>
<xsl:sequence select="()"/> <!-- пустая последовательность -->
</xsl:catch>
<xsl:catch>
<xsl:message>Other error</xsl:message>
</xsl:catch>
</xsl:try>

Встроенные ошибки (примеры):

КодОписание
err:XTDE0050Ошибка в xsl:message terminate="yes"
err:FODC0002Ошибка загрузки документа (doc())
err:FORG0001Ошибка приведения типа (например, xs:integer('abc'))
err:XPDY0002Обращение к контекстному узлу вне контекста

✅ Можно генерировать ошибки через <xsl:error> (Saxon-расширение) или error() в XPath.


🔹 Часть 5. Переменные, параметры, функции, типы, модульность и потоковая обработка

🔸 5.1 Переменные и параметры

Объявление

ЭлементОбласть видимостиОбяз. атрибутыОсобенности
<xsl:variable>Глобальная (дочерний <xsl:stylesheet>) или локальная (внутри шаблона/инструкции)nameНеизменяема после инициализации.
<xsl:param>Глобальная или локальная (в <xsl:template>, <xsl:function>, <xsl:iterate>)nameМожет быть переопределён при вызове (xsl:with-param, xsl:with-param tunnel).

Атрибуты (общие для xsl:variable/xsl:param):

АтрибутЗначенияПримечания
namexs:QNameОбязательный.
selectXPath-выражениеИнициализация. Если указано — тело игнорируется.
asSequenceType (XSLT 2.0+)Ограничение типа (строгая типизация). Проверяется при присвоении.
static"yes", "no" (XSLT 3.0)Статическая переменная/параметр — вычисляется на этапе компиляции. Доступна в use-when.
tunnel"yes", "no" (только для <xsl:param>)Параметр передаётся «сквозь» промежуточные шаблоны.
required"yes", "no" (XSLT 3.0)Обязательный параметр: если не передан — ошибка.
visibility"public", "private", "final", "abstract", "hidden" (XSLT 3.0, глобальные)Контроль доступа в пакетах.

Важные правила:

  • Глобальная переменная/параметр вычисляется лениво (при первом обращении) и один раз.
  • Локальная переменная вычисляется при входе в блок.
  • Тип xs:untypedAtomic — промежуточный тип строк без пространства имён (используется при чтении XML без схемы).
  • Если as указан и select отсутствует, инициализация — пустая последовательность ().
Примеры:
<!-- Глобальная строго типизированная переменная -->
<xsl:variable name="max-length" as="xs:integer" select="100"/>

<!-- Локальный параметр с типом и значением по умолчанию -->
<xsl:param name="lang" as="xs:string" select="'en'"/>

<!-- Статический параметр (доступен в use-when) -->
<xsl:param name="debug" static="yes" as="xs:boolean" select="true()"/>

🔸 5.2 Пользовательские функции (<xsl:function>)

АтрибутОбяз.ЗначенияПояснение
namexs:QNameИмя функции (обычно с префиксом, отличным от xsl:).
asSequenceTypeТип возвращаемого значения.
override"yes", "no" (устаревшее)Разрешить переопределение импортированной функции.
visibility"public", "private", "final", "abstract", "hidden" (XSLT 3.0)Контроль доступа.
override-extension-function"yes", "no"Разрешить перекрытие встроенных функций (например, math:sin). Опасно.
streamability"yes", "no", "shallow-descent" и др.Поддержка потоковой обработки.
saxon:memo-function"yes", "no" (Saxon)Мемоизация (кеширование результатов по аргументам).
saxon:lazy"yes" (Saxon)Ленивая оценка результата (для больших последовательностей).

Тело функции:

  • Обязательно содержит <xsl:param> (аргументы), затем — последовательность инструкций или xsl:sequence.
  • Возвращаемое значение — результат последней инструкции или явно через <xsl:sequence select="..."/>.
Пример:
<xsl:function name="my:format-price" as="xs:string">
<xsl:param name="amount" as="xs:decimal"/>
<xsl:param name="currency" as="xs:string" select="'USD'"/>
<xsl:sequence select="concat(format-number($amount, '¤#,##0.00'), ' ', $currency)"/>
</xsl:function>

Вызов: my:format-price(29.99, 'RUB').

⚠️ Ограничения:

  • Функции не имеют доступа к контекстному узлу (., position(), last() запрещены).
  • Не могут модифицировать глобальные переменные (чистые функции).
  • В XSLT 3.0 допустимы higher-order functions (функции как аргументы/возврат).

🔸 5.3 Типы данных и строгая типизация

XSLT 3.0 поддерживает полную систему типов XPath 3.1 (на основе XSD 1.1 + расширения).

Иерархия типов (упрощённо):

item()
├── node()
│ ├── document-node()
│ ├── element()
│ ├── attribute()
│ ├── text()
│ ├── comment()
│ └── processing-instruction()
├── function(*)
├── map(*)
├── array(*)
└── xs:anyAtomicType
├── xs:untypedAtomic
├── xs:string
│ ├── xs:normalizedString
│ └── xs:token
│ ├── xs:language, xs:NMTOKEN и др.
├── xs:boolean
├── xs:decimal
│ ├── xs:integer
│ ├── xs:long, xs:int, xs:short, xs:byte
│ └── xs:nonNegativeInteger и др.
├── xs:float, xs:double
├── xs:dateTime, xs:date, xs:time, xs:gYearMonth и др.
├── xs:QName, xs:NOTATION
└── xs:anyURI, xs:base64Binary, xs:hexBinary

SequenceType (синтаксис в as):

КонструкцияПримерыЗначение
Txs:string, element(book)Ровно один элемент типа T
T?xs:integer?, node()?0 или 1 элементов
T*item()*, xs:string*0 или более
T+attribute(id)+1 или более
empty-sequence()Только пустая последовательность
(T1, T2, T3)(xs:string, xs:integer)Точная последовательность из 3 элементов заданных типов

✅ Типизация помогает:

  • Выявлять ошибки на этапе компиляции (если включено: xsl:stylesheet/@schema-aware="yes" в Saxon-EE),
  • Оптимизировать выполнение (например, xs:integer → машинное целое).

🔸 5.4 Модульность: импорт, пакеты, пакетные зависимости (XSLT 3.0)

<xsl:import> и <xsl:include>

<xsl:import><xsl:include>
ПриоритетНизкий (переопределяется основной таблицей)Равный основной таблице
РасположениеТолько в начале, до любых объявленияПосле <xsl:import>, до остальных
Повторное включениеЗапрещено (ошибка)Разрешено, но не рекомендуется

Пакеты (<xsl:package>)

  • Единица модульности в XSLT 3.0.
  • Корневой элемент — <xsl:package> (вместо <xsl:stylesheet>).
  • Имеет name, package-version, visibility.
<xsl:use-package>
<xsl:use-package name="http://example.com/lib" package-version="1.0">
<xsl:accept component="function" names="lib:format-date"/>
<xsl:accept component="template" names="lib:toc"/>
<xsl:override>
<xsl:function name="lib:format-date" override="yes">
<!-- переопределение -->
</xsl:function>
</xsl:override>
</xsl:use-package>
<xsl:expose>
<xsl:expose component="function" names="my:format-price" visibility="public"/>
<xsl:expose component="variable" names="my:config" visibility="private"/>

✅ Пакеты позволяют:

  • Изолировать реализацию (visibility="private"),
  • Управлять версиями,
  • Избегать конфликтов имён.

🔸 5.5 Аккумуляторы (Accumulators, XSLT 3.0)

Механизм для поддержания состояния при потоковой обработке (streaming).

Объявление:

<xsl:accumulator name="section-depth" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="section" select="$value + 1"/>
<xsl:accumulator-rule match="section/section" select="$value - 1"/>
</xsl:accumulator>
АтрибутЗначенияПояснение
namexs:QNameИмя аккумулятора.
asSequenceTypeТип значения.
initial-valueXPathНачальное значение ($value — предыдущее).
streamable"yes", "no"Обязателен для streaming.
phase"start", "end"Когда обновлять: при входе в узел (start) или выходе (end). По умолчанию — end.

Использование:

  • В шаблоне: accumulator-before('section-depth'), accumulator-after('section-depth').
  • В xsl:apply-templates: use-accumulators="yes".

✅ Аккумуляторы — единственный способ доступа к «предыдущему состоянию» в потоковом режиме. ❌ Нельзя использовать preceding::, //, xsl:key, глобальные переменные.


🔸 5.6 Потоковая обработка (Streaming, XSLT 3.0)

Цель: обработка XML-документов больше, чем RAM, за один проход.

Требования:

  • Процессор: Saxon-EE (единственный полноценный поддерживаемый).
  • xsl:stylesheet/@streamable="yes" или <xsl:mode streamable="yes"/>.
  • Все шаблоны и функции — потоковые.

Правила потоковости (упрощённо):

✅ Разрешено:

  • child::, attribute::, self::node(),
  • xsl:apply-templates select="child::node()",
  • xsl:iterate, xsl:accumulator,
  • xsl:copy, xsl:element, xsl:attribute (без ссылок назад),
  • xsl:if, xsl:choose — если test потоковый.

❌ Запрещено:

  • //, descendant::, ancestor::, preceding::, following::,
  • position() > 1, last(), count(//*),
  • id(), key(),
  • $global-variable,
  • document(), doc(),
  • xsl:for-each-group (кроме group-adjacent в особых случаях).

Пример потокового шаблона:

<xsl:mode streamable="yes"/>
<xsl:template match="book" streamable="yes">
<book id="{@id}" title="{title}"/>
</xsl:template>

📊 Скорость: 50–200 МБ/с на SSD, потребление памяти — O(1) по глубине вложенности.


🔹 Часть 6. Встроенные функции: полный справочник

🔸 6.1 Последовательности и агрегация

ФункцияСигнатураВерсияПояснение
empty($seq)item()* → xs:booleanXPath 2.0true() если последовательность пуста.
exists($seq)item()* → xs:booleanXPath 2.0true() если не пуста.
count($seq)item()* → xs:integerXPath 1.0Число элементов.
distinct-values($seq, $collation?)xs:anyAtomicType* [, xs:string] → xs:anyAtomicType*XPath 2.0Уникальные значения (с учётом коллации).
index-of($seq, $value, $collation?)xs:anyAtomicType*, xs:anyAtomicType [, xs:string] → xs:integer*XPath 2.0Позиции вхождений (1-based).
insert-before($seq, $pos, $val)item()*, xs:integer, item()* → item()*XPath 2.0Вставка перед позицией $pos.
remove($seq, $pos)item()*, xs:integer → item()*XPath 2.0Удаление элемента.
reverse($seq)item()* → item()*XPath 2.0Разворот.
subsequence($seq, $start, $len?)item()*, xs:double, xs:double? → item()*XPath 1.0Подпоследовательность (1-based, $len не обяз.).
unordered($seq)item()* → item()*XPath 2.0Указывает, что порядок не важен (оптимизация).

⚠️ count(//node()) не streamable — требует полной загрузки.


🔸 6.2 Строки

ФункцияСигнатураВерсияПояснение
string($arg?)item()? → xs:stringXPath 1.0Приведение к строке. Без аргумента — string(.).
string-length($arg?)xs:string? → xs:integerXPath 1.0Длина в символах Unicode (не байтах).
normalize-space($arg?)xs:string? → xs:stringXPath 1.0Удаление leading/trailing whitespace, замена внутренних #x20.
upper-case($arg)xs:string → xs:stringXPath 2.0Приведение к верхнему регистру (по Unicode).
lower-case($arg)xs:string → xs:stringXPath 2.0К нижнему.
contains($haystack, $needle, $collation?)xs:string, xs:string, xs:string? → xs:booleanXPath 1.0Проверка подстроки.
starts-with($str, $prefix, $collation?)xs:string, xs:string, xs:string? → xs:booleanXPath 1.0Начинается с…
ends-with($str, $suffix, $collation?)xs:string, xs:string, xs:string? → xs:booleanXPath 2.0Заканчивается на…
substring($str, $start, $len?)xs:string, xs:double, xs:double? → xs:stringXPath 1.0Подстрока (1-based, $start может быть дробным — интерполяция).
substring-before($str, $substr, $collation?)xs:string, xs:string, xs:string? → xs:stringXPath 1.0Часть до первого вхождения.
substring-after($str, $substr, $collation?)xs:string, xs:string, xs:string? → xs:stringXPath 1.0Часть после.
translate($str, $map1, $map2)xs:string, xs:string, xs:string → xs:stringXPath 1.0Поэлементная замена (аналог tr).
codepoints-to-string($cp)xs:integer* → xs:stringXPath 2.0Из кодовых точек Unicode.
string-to-codepoints($str)xs:string → xs:integer*XPath 2.0В кодовые точки.
matches($input, $pattern, $flags?)xs:string, xs:string, xs:string? → xs:booleanXPath 2.0Regex-совпадение.
replace($input, $pattern, $replacement, $flags?)xs:string, xs:string, xs:string, xs:string? → xs:stringXPath 2.0Regex-замена. $1, $2 — группы.
tokenize($input, $pattern, $flags?)xs:string?, xs:string, xs:string? → xs:string*XPath 2.0Разбиение по регулярке.
analyze-string($input, $pattern, $flags?)xs:string, xs:string, xs:string? → document-node()XPath 2.0Возвращает XML-структуру совпадений (альтернатива xsl:analyze-string).

matches('abc', '^\p{L}+$') — проверка, что строка состоит только из букв.


🔸 6.3 Числа и математика

ФункцияСигнатураВерсияПояснение
number($arg?)item()? → xs:doubleXPath 1.0Приведение к числу.
abs($arg)xs:double → xs:doubleXPath 2.0Модуль.
ceiling($arg)xs:double → xs:doubleXPath 1.0Округление вверх.
floor($arg)xs:double → xs:doubleXPath 1.0Вниз.
round($arg, $precision?)xs:double, xs:integer? → xs:doubleXPath 2.0Округление (банковское).
round-half-to-even($arg, $precision?)xs:double, xs:integer? → xs:doubleXPath 2.0Округление «банкира».
sum($seq, $zero?)xs:anyAtomicType*, xs:anyAtomicType? → xs:anyAtomicTypeXPath 2.0Сумма. $zero — значение при пустой последовательности.
avg($seq)xs:anyAtomicType* → xs:anyAtomicTypeXPath 2.0Среднее.
min($seq, $collation?)xs:anyAtomicType* [, xs:string] → xs:anyAtomicTypeXPath 2.0Минимум.
max($seq, $collation?)xs:anyAtomicType* [, xs:string] → xs:anyAtomicTypeXPath 2.0Максимум.
math:pi()→ xs:doubleXPath 3.0π.
math:exp($arg)xs:double → xs:doubleXPath 3.0e^x.
math:log($arg)xs:double → xs:doubleXPath 3.0Натуральный логарифм.
math:pow($base, $exp)xs:double, xs:double → xs:doubleXPath 3.0Возведение в степень.
math:sin($arg), math:cos($arg), math:tan($arg)xs:double → xs:doubleXPath 3.0Тригонометрия (аргумент — радианы).
math:sqrt($arg)xs:double → xs:doubleXPath 3.0Квадратный корень.

⚠️ sum(//price) не streamable, но sum(xsl:stream(//price)) — streamable в XSLT 3.0 (с аккумулятором).


🔸 6.4 Дата и время

ФункцияСигнатураВерсияПояснение
current-dateTime()→ xs:dateTimeXPath 2.0Текущие дата и время (с часовым поясом).
current-date()→ xs:dateXPath 2.0Только дата.
current-time()→ xs:timeXPath 2.0Только время.
adjust-dateTime-to-timezone($dt, $tz?)xs:dateTime, xs:dayTimeDuration? → xs:dateTimeXPath 2.0Смещение часового пояса.
days-from-duration($dur)xs:duration → xs:integerXPath 2.0Число дней в xs:dayTimeDuration.
hours-from-dateTime($dt)xs:dateTime → xs:integerXPath 2.0Часы (0–23).
format-dateTime($value, $picture, $lang?, $calendar?, $place?)xs:dateTime, xs:string, xs:string?, xs:string?, xs:string? → xs:stringXPath 3.0Форматирование даты (ICU-подобный синтаксис).
parse-ietf-date($str)xs:string → xs:dateTimeXPath 3.1Разбор Thu, 20 Nov 2025 12:34:56 GMT.
Пример format-dateTime:
format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01][Z00:00:00]', 'en', (), ())
→ "2025-11-20T12:34:56+05:00"

🔸 6.5 Узлы и навигация

ФункцияСигнатураВерсияПояснение
node-name($node)node()? → xs:QNameXPath 2.0Имя узла (для элементов/атрибутов); () для текста/комментариев.
name($node?)node()? → xs:stringXPath 1.0Строка вида prefix:local или local.
local-name($node?)node()? → xs:stringXPath 1.0Только локальная часть.
namespace-uri($node?)node()? → xs:stringXPath 1.0URI пространства имён.
root($node?)node()? → node()XPath 2.0Корневой узел документа/фрагмента.
base-uri($node?)node()? → xs:anyURIXPath 2.0Базовый URI (из xml:base).
document-uri($node)node() → xs:anyURI?XPath 2.0URI документа (только для корневых узлов).
id($idvals, $node?)xs:string*, node()? → element()*XPath 1.0Поиск по @xml:id или @id (если DTD/схема определяет ID).
idref($idrefvals, $node?)xs:string*, node()? → node()*XPath 2.0Обратный id().
key($name, $keyvals, $top?)xs:string, xs:anyAtomicType*, node()? → node()*XSLT 1.0Поиск по <xsl:key>. $top — узел для поиска (по умолчанию — корень).
unparsed-text($href, $encoding?)xs:string, xs:string? → xs:stringXSLT 2.0Чтение текстового файла (не XML).
unparsed-text-lines($href, $encoding?)xs:string, xs:string? → xs:string*XPath 3.0По строкам.
unparsed-text-available($href, $encoding?)xs:string, xs:string? → xs:booleanXSLT 2.0Проверка существования.

⚠️ key() не streamable — требует индекса в памяти.


🔸 6.6 Карты и массивы (XSLT 3.0 / XPath 3.1)

Карты (map:*)

ФункцияСигнатураПояснение
map:contains($map, $key)map(*), xs:string → xs:booleanЕсть ли ключ.
map:get($map, $key)map(*), xs:string → item()*Получить значение.
map:put($map, $key, $value)map(*), xs:string, item()* → map(*)Новый map с добавленной парой.
map:remove($map, $key)map(*), xs:string → map(*)Удалить ключ.
map:keys($map)map(*) → xs:string*Все ключи.
map:size($map)map(*) → xs:integerЧисло пар.
map:for-each($map, $function)map(*), function(xs:string, item()*) as item()* → item()*Итерация (higher-order).
map:merge($maps, $options?)map(*)*, map(xs:string, item()*)? → map(*)Объединение карт (конфликты: use-first, use-last, combine, combine-into-array).

Массивы (array:*)

ФункцияСигнатураПояснение
array:size($array)array(*) → xs:integerДлина.
array:get($array, $index)array(*), xs:integer → item()*Элемент (1-based).
array:append($array, $value)array(*), item()* → array(*)Добавить в конец.
array:subarray($array, $start, $len?)array(*), xs:integer, xs:integer? → array(*)Подмассив.
array:head($array)array(*) → item()*Первый элемент.
array:tail($array)array(*) → array(*)Все, кроме первого.
array:reverse($array)array(*) → array(*)Разворот.
array:join($arrays)array(*)* → array(*)Конкатенация массивов.
array:for-each($array, $function)array(*), function(item()*) as item()* → item()*Итерация.
array:filter($array, $function)array(*), function(item()*) as xs:boolean → array(*)Фильтрация.
array:fold-left($array, $zero, $function)array(*), item()*, function(item()*, item()*) as item()* → item()*Левая свёртка.

map:merge(($m1, $m2), map{'duplicates': 'combine'}) — объединение с сохранением дублей как array(*).


🔸 6.7 Потоки и отладка

ФункцияСигнатураВерсияПояснение
trace($value, $label)item()*, xs:string → item()*XPath 3.0Вывод значения в лог (Saxon — в stderr).
error($code?, $description?, $error-object?)xs:QName?, xs:string?, item()? → noneXPath 3.0Генерация ошибки.
xsl:original()→ item()*XSLT 3.0Внутри xsl:override — доступ к оригинальной реализации.
xsl:evaluate($expr, $context-item?, $base-uri?, $namespace-context?, $schema-aware?)xs:string, item()?, xs:anyURI?, element(), xs:boolean? → item()*XSLT 3.0Динамическая оценка XPath-выражения (потенциально опасна).

⚠️ xsl:evaluate отключает проверку типов и оптимизации — используйте осторожно.


🔹 Часть 7. Практические шаблоны и идиомы XSLT

🔸 7.1 Identity Transform (полное копирование)

XSLT 1.0 / 2.0 / 3.0
Базовый шаблон для любых изменяющих трансформаций («изменяй только нужное»).

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:mode on-no-match="shallow-copy"/>

<!-- или явно: -->
<!--
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
-->
</xsl:stylesheet>

✅ XSLT 3.0: <xsl:mode on-no-match="shallow-copy"/> — короче, быстрее, поддерживает потоковость.


🔸 7.2 Фильтрация узлов при копировании

Удалить элементы по условию:

<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="debug"/> <!-- удалить <debug> -->
<xsl:template match="comment()"/> <!-- удалить комментарии -->
<xsl:template match="processing-instruction()"/> <!-- удалить PI -->
<xsl:template match="@temp"/> <!-- удалить атрибут temp -->

Сохранить только определённые элементы:

<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="* | @* | text()"/>
</xsl:copy>
</xsl:template>
<!-- отключить копирование по умолчанию для нежелательных узлов -->
<xsl:template match="script | style | @class"/>

🔸 7.3 Преобразование атрибутов в элементы («атрибуты → дочерние элементы»)

Часто требуется для нормализации в NoSQL/JSON.

<xsl:template match="*">
<xsl:copy>
<xsl:for-each select="@*">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>

Результат:
<book id="123" lang="en">…</book><book><id>123</id><lang>en</lang>…</book>

⚠️ Конфликт имён: если атрибут и элемент имеют одинаковое имя — потребуется префикс (attr-), например: <xsl:element name="attr-{local-name()}"/>.


🔸 7.4 «Выпрямление» иерархии (flattening)

Преобразование вложенных структур в плоский список.

Пример: из <chapter><section><subsection>…<section level="1">, <section level="2"> и т.д.

<xsl:template match="*">
<xsl:variable name="level" as="xs:integer">
<xsl:number level="multiple" count="chapter | section | subsection"/>
</xsl:variable>
<section level="{$level}">
<xsl:apply-templates select="@* | node()"/>
</section>
<xsl:apply-templates select="*"/>
</xsl:template>

✅ Использует <xsl:number level="multiple"/> — стандартный механизм нумерации.


🔸 7.5 Глубокое объединение (deep merge) XML

Объединение двух XML-документов с приоритетом «новый поверх старого».

<xsl:function name="my:deep-merge" as="node()*">
<xsl:param name="primary" as="node()*"/>
<xsl:param name="secondary" as="node()*"/>

<xsl:for-each-group select="$primary, $secondary" group-by="node-name(.)">
<xsl:choose>
<xsl:when test="current-group()[2] and self::element()">
<xsl:copy>
<!-- объединить атрибуты: из secondary, затем primary (переопределение) -->
<xsl:copy-of select="current-group()[2]/@*"/>
<xsl:copy-of select="current-group()[1]/(@* except @*[name() = current-group()[2]/@*/name()])"/>
<!-- рекурсивно объединить детей -->
<xsl:sequence select="
my:deep-merge(
current-group()[1]/node(),
current-group()[2]/node()
)"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()[1]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>

⚠️ Не обрабатывает текстовые узлы (требует уточнения стратегии: конкатенация / замена).


🔸 7.6 XML ↔ JSON

XML → JSON (XSLT 3.0)

<xsl:output method="json" indent="yes"/>

<xsl:template match="/">
<xsl:sequence select="my:xml-to-json(*)"/>
</xsl:template>

<xsl:function name="my:xml-to-json" as="item()*">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="$node instance of element()">
<xsl:map>
<xsl:map-entry key="name($node)">
<xsl:sequence select="
if ($node/*) then
array { $node/* ! my:xml-to-json(.) }
else if ($node/@*) then
map:merge((
map { 'text': string($node) },
$node/@* ! map:entry(local-name(), string(.))
))
else
string($node)
"/>
</xsl:map-entry>
</xsl:map>
</xsl:when>
</xsl:choose>
</xsl:function>

Пример входа:

<book id="123">
<title>XSLT Guide</title>
<author>Timur</author>
</book>

{
"book": [
{
"text": "",
"id": "123",
"title": ["XSLT Guide"],
"author": ["Timur"]
}
]
}

JSON → XML (обратное преобразование)

<xsl:template match="map(*)" mode="json-to-xml">
<xsl:for-each select="map:keys(.)">
<xsl:element name="{.}">
<xsl:apply-templates select="map:get(., .)" mode="json-to-xml"/>
</xsl:element>
</xsl:for-each>
</xsl:template>

<xsl:template match="array(*)" mode="json-to-xml">
<xsl:for-each select="?*">
<item>
<xsl:apply-templates select="." mode="json-to-xml"/>
</item>
</xsl:for-each>
</xsl:template>

<xsl:template match="xs:string | xs:double | xs:boolean" mode="json-to-xml">
<xsl:value-of select="."/>
</xsl:template>

✅ Требует method="adaptive" или обработки через xsl:sequence.


🔸 7.7 Отладка и профилирование

Встроенные средства:

  • <xsl:message terminate="no">Debug: {$var}</xsl:message>
  • <xsl:comment>Debug: <xsl:value-of select="$var"/></xsl:comment>
  • trace($value, 'label') → Saxon выводит в stderr: [label] value

Профилирование в Saxon:

java -cp saxon-he.jar net.sf.saxon.Transform \
-xsl:transform.xsl -s:input.xml -t --profile:profile.xml

Результат — XML-отчёт с временем выполнения каждого шаблона/функции.

Проверка потоковости:

java -cp saxon-ee.jar net.sf.saxon.Transform \
-xsl:transform.xsl -s:input.xml -val:strict --streaming:on

Saxon выдаст ошибку, если трансформация не streamable.


🔸 7.8 Оптимизация производительности

ПриёмЭффектПримечание
Использовать xsl:key вместо //book[@id=$id]O(1) vs O(n)Требует индекса в памяти.
Избегать // в match и selectОсобенно в streaming.
Предпочитать xsl:sequence перед xsl:copy-of для нетипизированных данныхМеньше аллокацийxsl:copy-of копирует узлы, xsl:sequence — передаёт ссылки/значения.
Использовать tunnel="yes" вместо явной передачи параметровЧистота кодаНет overhead’а (Saxon оптимизирует).
Статические параметры (static="yes") для use-whenУсловная компиляцияУменьшает размер скомпилированной таблицы.
xsl:mode streamable="yes" + аккумуляторыПамять O(1)Только Saxon-EE.
Избегать disable-output-escaping="yes"Безопасность, потоковостьЗаменить на CDATA или character-map.

Пример кэширования через saxon:memo-function:

<xsl:function name="my:expensive" saxon:memo-function="yes" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<!-- дорогостоящее вычисление -->
</xsl:function>

⚠️ Мемоизация потребляет память — используйте для функций с малым числом уникальных аргументов.