对于下面的(人为的)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 中实现这一点?
你可以在这里使用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:next-match
的任何位置使用xsl:apply-imports
<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>
我认为正确的答案是 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预处理器包含机制的一种形式。您可以将其视为转换之间的遗传机制。