#xslt #xslt-1.0 #xslt-2.0
#xslt #xslt-1.0 #xslt-2.0
Вопрос:
Для (надуманного) примера HTML ниже:
<div>
<p>lorem <a href="lorem.html" target="_blank">ipsum</a></p>
<a href="foo.html" target="top">foo</a>
<p><img src="foo.jpg" class="bar"/></p>
<img src="bar.jpg" class="bar"/>
</div>
Я пытаюсь написать преобразование XSLT 1.0, которое:
- белые списки верхнего уровня
<p>
href
атрибут белых списков для<a>
src
атрибут белых списков для<img>
- переносит верхний уровень
<a>
и<img>
в<p>...</p>
В идеале это было бы сделано способом, позволяющим добавлять больше элементов и атрибутов.
Ожидаемый результат:
<div>
<p>lorem <a href="lorem.html">ipsum</a></p>
<p><a href="foo.html">foo</a></p>
<p><img src="foo.jpg"/></p>
<p><img src="bar.jpg"/></p>
</div>
Следующий XSLT 2.0 работает благодаря <xsl:next-match>
:
Скрипка: https://xsltfiddle .liberty-development.net/6r5Gh3p:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/div">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- whitelist <p> as top-level element -->
<xsl:template match="/div/p">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- coerce top-level <img> and <a> as children <p> -->
<xsl:template match="/div/img|/div/a">
<p><xsl:next-match/></p>
</xsl:template>
<!-- whitelist href attribute for <a> -->
<xsl:template match="a">
<xsl:copy>
<xsl:copy-of select="@href"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- whitelist src attribute for <img> -->
<xsl:template match="img">
<xsl:copy>
<xsl:copy-of select="@src"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
В XSLT 1.0 нет <next-match>
, и использование шаблона ниже совпадает только один раз, поэтому <a>
и <img>
попадают в <p>
, но их атрибуты не попадают в белый список:
Скрипка: https://xsltfiddle .liberty-development.net/94rmq6r
<xsl:template match="/div/img|/div/a">
<p>
<xsl:copy><xsl:apply-templates/></xsl:copy>
</p>
</xsl:template>
Вывод:
<div>
<p>lorem <a href="lorem.html">ipsum</a></p>
<p><a>foo</a></p>
<p><img src="foo.jpg"/></p>
<p><img/></p>
</div>
Как это можно выполнить в XSLT 1.0?
Ответ №1:
Вы могли бы использовать xsl:import
и xsl:apply-imports
здесь.
Для начала вы должны поместить свои шаблоны «белого списка» в отдельный файл XSLT (назовите его «Whitelist.xslt»)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<!-- whitelist <p> as top-level element -->
<xsl:template match="/div/p">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- whitelist href attribute for <a> -->
<xsl:template match="a">
<xsl:copy>
<xsl:copy-of select="@href"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- whitelist src attribute for <img> -->
<xsl:template match="img">
<xsl:copy>
<xsl:copy-of select="@src"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Затем ваш основной XSLT может импортировать это и использовать xsl:apply-imports
везде, где вы использовали xsl:next-match
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:import href="Whitelist.xslt" />
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/div">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- coerce top-level <img> and <a> as children <p> -->
<xsl:template match="/div/img|/div/a">
<p><xsl:apply-imports/></p>
</xsl:template>
</xsl:stylesheet>
При импортированной таблице стилей шаблоны внутри имеют более низкий приоритет, чем в основной таблице стилей, поэтому основной шаблон всегда будет сопоставляться первым.
РЕДАКТИРОВАТЬ: в сторону… Я знаю, что ваш пример надуманный, но для этого конкретного случая вы можете переписать его без следующих совпадений или применения импорта, например…
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/div|/div/p">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="/div/img|/div/a">
<p>
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</p>
</xsl:template>
<xsl:template match="a|img">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="a/@href|img/@src">
<xsl:copy />
</xsl:template>
<xsl:template match="@*" />
</xsl:stylesheet>
Комментарии:
1. В этом конкретном случае есть ограничения, о которых я не упоминал, которые делают предпочтительным не оставлять процессор для самостоятельной загрузки таблиц стилей, а также не размещать их, поэтому «в стороне» может быть очень полезным, в зависимости от того, как он выдержит дальнейший взлом. В любом случае, принято. Спасибо!
2. Другой возможностью было бы использовать «режимы» в шаблоне. См. xsltfiddle . liberty-development.net/gWvjQfx
Ответ №2:
Я думаю, что правильный ответ заключается в том, что в XSLT 1.0 нет эквивалентности для семантики инструкций XSLT 2.0 xsl:next-match
. Что как-то ожидается. Из спецификации:
Правило шаблона, которое используется для переопределения другого правила шаблона (см. 6.4 Разрешение конфликтов для правил шаблонов), может использовать
xsl:apply-imports
xsl:next-match
инструкцию или для вызова переопределенного правила шаблона.xsl:apply-imports
Инструкция учитывает только правила шаблонов в импортированных модулях таблиц стилей;xsl:next-match
инструкция учитывает все другие правила шаблонов с более низким приоритетом импорта и / или приоритетом. Обе инструкции вызовут встроенное правило шаблона для узла (см. 6.6 Встроенные правила шаблонов), если другое правило шаблона не найдено.
Итак, сама спецификация дает вам связь и различие между обеими инструкциями: вы не можете знать, какой шаблон появился последним в порядке объявления среди всех тех, которые остались после разрешения конфликта. Также стоит отметить, что импортированные модули таблиц стилей — это не просто форма механизма включения препроцессора C. Вы можете считать это механизмом передачи данных между преобразованиями.