迭代器通过嵌套xsl:for-each的跟踪

问题描述 投票:1回答:3

我要输入以下内容:

<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()仅考虑相同级别的元素。但是我该怎么办?

xslt-2.0 xpath-2.0
3个回答
1
投票

此转换

<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>

1
投票

在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>

0
投票

我想出了一个解决方案:使用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>
© www.soinside.com 2019 - 2024. All rights reserved.