在 XSLT 1.0 中应用多个模板,如 XSLT 2.0 的<next-match>



对于下面的(人为的)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:importxsl: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-importsxsl:next-match指令来调用 覆盖的模板规则。仅限xsl:apply-imports指令 考虑导入样式表模块中的模板规则;这xsl:next-match指令考虑所有其他模板规则 较低的导入优先级和/或优先级。两个指令都将调用 节点的内置模板规则(请参阅6.6 内置模板规则),如果未找到其他模板规则。

因此,规范本身为您提供了两个指令之间的关系和区别:您无法知道在冲突解决后离开的所有模板中,模板在声明顺序中最后发生。还值得注意的是,导入的样式表模块不仅仅是C预处理器包含机制的一种形式。您可以将其视为转换之间的遗传机制。

最新更新