以下应该是简单的或不可能的,但是现在我无法知道如何,所以我问。在我的XSLT中,我有生成元素的模板,然后应该再次进行转换。更精确的是,只要模板输出原始输入中将要进行变换的元素,就应该再次对其进行变换。因此,无限循环是可能的,但是要通过仔细设计模板来避免。以此为例:
input.xml中
<?xml version="1.0" encoding="utf-8" ?>
<example>
<a />
<b />
</example>
transform.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()"><xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy></xsl:template>
<xsl:template match="a">a</xsl:template>
<xsl:template match="b">
<B>b <a /></B>
</xsl:template>
</xsl:transform>
电流的Output.xml
<?xml version="1.0"?>
<example>
a
<B>b <a/></B>
</example>
期望-的Output.xml
<?xml version="1.0"?>
<example>
a
<B>b a</B>
</example>
只需一次转换即可实现这一目标的最佳解决方案是什么?
答案 0 :(得分:2)
这是一个两遍转换,可以产生想要的结果:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)/*"/>
<xsl:apply-templates select="$vPass1" mode="pass2"/>
</xsl:template>
<xsl:template match="content">
<wrapper>
<replace />
<xsl:apply-templates select="@*|node()" />
</wrapper>
</xsl:template>
<xsl:template match="replace">
<xsl:text>
Hello world
</xsl:text>
</xsl:template>
<xsl:template match="@*|node()" mode="pass2">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<example>
<content>Lorem ipsum</content>
<content><replace /></content>
</example>
产生了想要的正确结果:
<example>
<wrapper>
Hello world
Lorem ipsum</wrapper>
<wrapper>
Hello world
Hello world
</wrapper>
</example>
请注意:
在XSLT 1.0中,应用模板的结果是臭名昭着的RTF(结果树片段),根据定义,除了使用xsl:copy-of
和标准字符串函数之外,它无法进一步访问和处理。
这就是为什么几乎每个XSLT 1.0处理器都提供特定于供应商的扩展函数xxx:node-set()
,它接受RTF并将其转换为“常规”树,可以使用任何XPath表达式访问后代。这里xxx
前缀必须绑定到vendor-specifix namespace-uri。
EXSLT ext:node-set()
由大多数XSLT处理器实现 - 因此它的使用保证了不同XSLT处理器之间的高度可移植性。
有关其他多遍转换示例,请参阅:
https://stackoverflow.com/a/3200026/36305
以及:
答案 1 :(得分:1)
它并不适用于所有情况,但有一种非常简单的方法可以实现您的要求。您只需为replace
模板命名,然后使用xsl:call-template
进行调用即可。这只需要对现有样式表进行一些小的更改:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()"><xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy></xsl:template>
<xsl:template match="content">
<wrapper>
<xsl:call-template name="replace"/>
<xsl:apply-templates select="@*|node()" />
</wrapper>
</xsl:template>
<xsl:template match="replace" name="replace">
Hello world
</xsl:template>
</xsl:transform>
答案 2 :(得分:0)
基于其他答案的输入,我想出了解决问题的方法。考虑作为更恰当的示例输入:
<?xml version="1.0" encoding="utf-8" ?>
<example>
<a />
<b />
<c />
</example>
元素<a />
,<b />
和<c />
在我的原始示例中与<replace />
类似,但如转换中所示,它们的模板每个都包含对它们的进一步引用:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:template match="@*|node()">
<xsl:variable name="this">
<xsl:apply-templates select="." mode="normal" />
</xsl:variable>
<xsl:apply-templates select="ext:node-set($this)" mode="normal" />
</xsl:template>
<xsl:template match="@*|node()" mode="normal">
<xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="a" mode="normal">a</xsl:template>
<xsl:template match="b" mode="normal">
<B>b <a /></B>
</xsl:template>
<xsl:template match="c" mode="normal">
<C>c <b /></C>
</xsl:template>
</xsl:transform>
当应用于示例输入时,此转换会生成我想要的结果:
<?xml version="1.0"?>
<example>
a
<B>b a</B>
<C>c <B>b a</B></C>
</example>
显然所有递归引用都已解决。虽然它是用一些更复杂的例子进行测试的,但我不确定它是否适用于所有情况。
这背后的想法如下:前两个模板用于复制。具有mode="normal"
的那个是简单的身份转换而没有递归的那个。使用模式是为了防止递归模板中的无限循环。
请告诉我,您对此有何看法以及是否存在明显缺陷!