我有一个输入XML:
<root>
<child>somevalue</child>
<child>othervalue</child>
</root>
需要这个输出:
<root>
<child>item1_somevalue</child>
<child>item2_othervalue</child>
</root>
我正在使用此模板:
<xsl:template match="*[local-name()='root' and namespace-uri()='']/*[local-name()='child' and namespace-uri()='']">
<xsl:element name="{local-name()}">
<xsl:text>item</xsl:text>
<xsl:value-of select="position()"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
得到这个结果:
<child>item2_somevalue</child>
<child>item4_othervalue</child>
为什么position()
返回预期值的两倍?
我现在不担心根标签。
必须使用XSLT 1.0。
答案 0 :(得分:3)
position()
函数有时会返回意外结果。乍一看,听起来应该返回context元素相对于其父容器的位置。因此,对于我们的示例输入XML:
<root>
<child>somevalue</child>
<child>othervalue</child>
</root>
...我们希望看到第一个position()
元素的child
为1
,第二个position()
为child
元素将是2
。
但是当我们针对该代码运行示例XSL时,我们得到以下输出:
<child>item2_somevalue</child>
<child>item4_othervalue</child>
关键是我们需要特别注意措辞。 position()
元素不仅计算元素,它计算节点 - 并且包含文本节点。因此,在上面的示例输入XML中,第一个child
元素实际上是root
下的第二个节点,因为它前面有一个文本节点(换行符和领先的空白)。并且,出于同样的原因,第二个child
元素实际上是root
下的第四个节点。所以position()
正在准确回归。
有几种方法可以解决这个问题。一个是明确计算child
个元素,就像jpmo22的帖子那样。另一种更灵活的方法是告诉XSL处理器忽略仅空白文本节点。我们可以通过在XSLT代码中添加顶级元素来实现:
<xsl:strip-space elements="*"/>
我们可以根据需要自定义元素列表,方法是将*
替换为元素名称列表。
如果我们只是在示例XSL代码中的strip-space
之前添加template
指令,我们就可以得到预期的结果:
<child>item1_somevalue</child>
<child>item2_othervalue</child>
match
语句中的XPath是错综复杂的。
<xsl:template match="*[local-name()='root' and namespace-uri()='']/*[local-name()='child' and namespace-uri()='']">
如果元素名称没有前缀,local-name
和name
会返回相同的结果。我们的示例输入XML没有前缀,因此没有理由使用local-name
。实际上,我们可以简单地使用所需的元素名称而不是*
。此外,我们的示例输入XML根本没有名称空间声明,因此没有理由使用namespace-uri
。
建议的简化:
<xsl:template match="root/child">
使用element
也有点费解。
<xsl:element name="{local-name()}">
我们已经知道我们正在使用child
个元素,因为这是我们在此模板中match
编辑的内容。我们可以更简单地在文字元素声明中使用元素名称,如下所示:
<child>
如果我们想要对代码进行泛化,我们也可以复制上下文元素:
<xsl:copy>
答案 1 :(得分:0)
我找到了解决方案。我将position()
更改为count(preceding::*[local-name()='child']) + 1
XSLT:
<xsl:template match="*[local-name()='child']">
<xsl:element name="{local-name()}">
<xsl:text>item</xsl:text>
<xsl:value-of select="count(preceding::*[local-name()='child']) + 1"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
给出正确的结果:
<child>item1_somevalue</child>
<child>item2_othervalue</child>