XSLT:多次添加数字和打印小计

Nov*_*der 1 xml xslt

我是XSLT的初学者,并且发现我不能只是将数字加到变量中并以任何方式改变它的值.

我有一个XML文档,其中包含我需要添加的数字列表,直到元素与特定属性值匹配,然后打印该数字将其重置为0并继续累加其余部分,直到我再次看到该特定属性.

例如,我有这个XML:

<list>
 <entry>
  <field type="num" value="189.5" />
 </entry>
 <entry>
  <field type="num" value="1.5" />
 </entry>
 <entry>
  <field type="summary" />
 </entry>
 <entry>
  <field type="num" value="9.5" />
 </entry>
 <entry>
  <field type="num" value="11" />
 </entry>
 <entry>
  <field type="num" value="10" />
 </entry>
 <entry>
  <field type="summary" />
 </entry>
</list>
Run Code Online (Sandbox Code Playgroud)

现在我希望我的XSLT打印出来:

189.5
1.5
#191#
9.5
11
10
#30.5#
Run Code Online (Sandbox Code Playgroud)

我已经读过,我可以通过使用sum()和条件来做到这一点.我知道如何使用for-each并相对指向元素,iam也可以通过简单地总结所有类型= num来使用sum(),但是如何仅将第一个num加起来直到type = summary出现,然后仅下一个sum从最后一个类型=摘要到下一个?

我希望这样的事情:

<xsl:for-each select="list/entry">
 <xsl:if test="field[@type='summary']">
  <!-- we are now at a type=summary element, now sum up -->
  #<xsl:value-of select="sum(WHAT_TO_PUT_HERE?)" />#
 </xsl:if>
 <xsl:if test="field[@type='num']">
  <xsl:value-of select="field/@value" />
 </xsl:if>
</xsl:for-each>
Run Code Online (Sandbox Code Playgroud)

感谢任何帮助.

Dim*_*hev 6

I.这是一个简单的,仅向前的解决方案 - 请注意,不使用反向轴,时间复杂度仅为O(N),空间复杂度仅为O(1).

这可能是所有解决方案中最简单,最快速的解决方案:

根本不需要怪异的复杂性或分组 ......

没有变量,没有键(没有空间用于缓存key->值),没有sum()......

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

  <xsl:template match="/*"><xsl:apply-templates select="*[1]"/></xsl:template>

  <xsl:template match="entry[field/@type = 'num']">
    <xsl:param name="pAccum" select="0"/>
    <xsl:value-of select="concat(field/@value, '&#xA;')"/>
    <xsl:apply-templates select="following-sibling::entry[1]">
      <xsl:with-param name="pAccum" select="$pAccum+field/@value"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="entry[field/@type = 'summary']">
    <xsl:param name="pAccum" select="0"/>
    <xsl:value-of select="concat('#', $pAccum, '#&#xA;')"/>  
    <xsl:apply-templates select="following-sibling::entry[1]"/>
  </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

这是流转换的一个示例 - 它不需要完整的XML文档树存在于内存中,并且可以用于处理无限长或无限长的文档.

在提供的源XML文档上应用转换时:

<list>
    <entry>
        <field type="num" value="189.5" />
    </entry>
    <entry>
        <field type="num" value="1.5" />
    </entry>
    <entry>
        <field type="summary" />
    </entry>
    <entry>
        <field type="num" value="9.5" />
    </entry>
    <entry>
        <field type="num" value="11" />
    </entry>
    <entry>
        <field type="num" value="10" />
    </entry>
    <entry>
        <field type="summary" />
    </entry>
</list>
Run Code Online (Sandbox Code Playgroud)

产生了想要的正确结果:

189.5
1.5
#191#
9.5
11
10
#30.5#
Run Code Online (Sandbox Code Playgroud)

II.更新

当在足够大的XML文档上运行并且使用不优化尾递归的XSLT处理器时,上面的转换会导致堆栈溢出,这是由于 <xsl:apply-templates>

下面是另一个转换,即使使用非常大的XML文档也不会导致堆栈溢出.同样,没有反向轴,没有键,没有"分组",没有条件指令,没有count(),没有<xsl:variable>......

而且,最重要的是,与基于密钥的"有效"Muenchian分组相比,当在具有105000(105000)行的XML文档上运行时,这种转换仅占后者的61%:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:apply-templates select=
  "*[1] | entry[field/@type = 'summary']/following-sibling::*[1]"/>
 </xsl:template>

  <xsl:template match="entry[field/@type = 'num']">
    <xsl:param name="pAccum" select="0"/>

    <xsl:value-of select="concat(field/@value, '&#xA;')"/>

    <xsl:apply-templates select="following-sibling::entry[1]">
        <xsl:with-param name="pAccum" select="$pAccum+field/@value"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="entry[field/@type = 'summary']">
    <xsl:param name="pAccum" select="0"/>

    <xsl:value-of select="concat('#', $pAccum, '#&#xA;')"/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

此外,通过仅替换每个元素名称,可以加快这种转换,使得Muenchian分组转换所花费的时间少于50%(即,使其快两倍以上). *

我们所有人都需要学习的经验教训:非关键解决方案有时可能比基于密钥的解决方案更有效.