我要输入以下内容:
<test>
<a>
<b />
<b />
</a>
<a>
<b />
<b />
</a>
</test>
并使用XSLT 2.0创建以下输出:
<items>
<item num="1">
<item num="2"/>
<item num="3"/>
</item>
<item num="4">
<item num="5"/>
<item num="6"/>
</item>
</items>
我知道这是错误的,但是首先,这是我当前的XSLT:
<xsl:template match="test">
<items>
<xsl:for-each select="a">
<item num="{position()}">
<xsl:for-each select="b">
<item num="{position()}"/>
</xsl:for-each>
</item>
</xsl:for-each>
</items>
</xsl:template>
这显然不是实现此目的的方法,因为position()
仅考虑相同级别的元素。但是我该怎么办?
此转换:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<items><xsl:apply-templates/></items>
</xsl:template>
<xsl:template match="a|b">
<xsl:variable name="vNum">
<xsl:number level="any" count="a|b"/>
</xsl:variable>
<item num="{$vNum}">
<xsl:apply-templates/>
</item>
</xsl:template>
</xsl:stylesheet>
当应用于所提供的XML文档时:
<test>
<a>
<b />
<b />
</a>
<a>
<b />
<b />
</a>
</test>
产生想要的正确结果:
<items>
<item num="1">
<item num="2"/>
<item num="3"/>
</item>
<item num="4">
<item num="5"/>
<item num="6"/>
</item>
</items>
在XSLT 2中,使用xsl:number
是正确的方法,如果您的XSLT处理器也支持XSLT 3,那么可以使用累加器https://www.w3.org/TR/xslt-30/#element-accumulator:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy" use-accumulators="item-count"/>
<xsl:accumulator name="item-count" as="xs:integer" initial-value="0">
<xsl:accumulator-rule match="test" select="0"/>
<xsl:accumulator-rule match="test/a | test/a/b" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="test">
<items>
<xsl:apply-templates/>
</items>
</xsl:template>
<xsl:template match="a | b">
<item num="{accumulator-before('item-count')}">
<xsl:apply-templates/>
</item>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/3NSSEvn
甚至可以在不支持xsl:number
的Saxon EE中使用流式传输:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy" use-accumulators="item-count" streamable="yes"/>
<xsl:accumulator name="item-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="test" select="0"/>
<xsl:accumulator-rule match="test/a | test/a/b" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="test">
<items>
<xsl:apply-templates/>
</items>
</xsl:template>
<xsl:template match="a | b">
<item num="{accumulator-before('item-count')}">
<xsl:apply-templates/>
</item>
</xsl:template>
</xsl:stylesheet>
我想出了一个解决方案:使用xsl:number任意级别并同时计算a和b:
<xsl:template match="test">
<items>
<xsl:for-each select="a">
<item>
<xsl:attribute name="num">
<xsl:number level="any" count="a|b"/>
</xsl:attribute>
<xsl:for-each select="b">
<item>
<xsl:attribute name="num">
<xsl:number level="any" count="a|b"/>
</xsl:attribute>
</item>
</xsl:for-each>
</item>
</xsl:for-each>
</items>
</xsl:template>