如何在XSLT中对多个节点的值进行求和

4
我需要一个循环到特定节点position()并计算这些节点的总和。例如:
  • 如果我的位置是2,那么节点[1] + 节点[2]
  • 如果我的位置是5,则节点[1] + 节点[2] + 节点[3] + 节点[4] + 节点[5]
在XSLT中如何实现?
有以下XML:
<?xml-stylesheet type="text/xsl" href="sum.xsl"?><root><FIDetail Node1="5" Node2="2" Node3="9" Node4="5" Node5="1" Node6="6" Node7="5" Node8="5" Node9="12" Node10="6" Node11="4" Node12="8" Node13="4" Node14="6" Node15="5" Node16="6" Node17="2" Node18="7" Node19="4" Node20="5" Node21="4" Node22="6" Node23="4" Node24="11" Node25="5" Node26="1" Node27="7" Node28="1" Node29="4" Node30="2" Node31="5" Node32="2" Node33="6" Node34="4" Node35="7" Node36="7" Node37="9" Node38="10" Node39="3" Node40="8" Node41="8" Node42="5" Node43="5" Node44="2" Node45="5" Node46="12" Node47="9" Node48="14" Node49="18" Node50="1"/></root>

我正在尝试展示以下输出

5 Node1的总和

7 Node1和Node2的总和

16 Node1、Node2和Node3的总和

.... Node1到Node50的总和

请问有人能帮我写出对应的XSL吗?

目前不起作用的XSL代码如下:

<?xml version="1.0" encoding="UTF-8"?>

    <xsl:for-each select="//FIDetail/@*">
        <br/>
        <xsl:value-of select="sum(preceding-sibling::*) + ."/>
        =
        <xsl:for-each select="preceding-sibling::*">
            <xsl:value-of select="name()"/> +
        </xsl:for-each>
        <xsl:value-of select="name()"/>
    </xsl:for-each>

</xsl:template>


Marc Gravell,谢谢,它正在工作,你太棒了,我很感激你的快速回复。 - Kedar
3个回答

3

求上下文节点和前面兄弟节点之和的XPath查询为. + sum(./preceding-sibling::*)

例如,如果您有以下XML ...

<a>
   <b id="a">1</b>
   <b id="b">2</b>
   <b id="c">3</b>
   <b id="d">4</b>
   <b id="e">5</b>
   <b id="f">6</b>
</a>

你可以使用以下表达式获取id="c"节点及其前面节点的总和:
/a/b[@id="c"]/(. + sum(./preceding-sibling::*))

+1. 我认为 sum(.| preceding-sibling::*) 更加优雅。 - Tomalak
1
@Kedar:从您最初的问题中并不清楚您在谈论属性。如果没有依赖于特定XSL转换器的实现细节,您无法实现您想要的内容。XML规范说明属性顺序是无关紧要的(第3.1章),XPath position()函数对于属性节点的行为也没有指定。 - jarnbjo

2

虽然不太美观,但它确实有效:

<table>
  <xsl:for-each select="@*">
    <tr>
      <xsl:variable name="pos" select="position()"/>
      <xsl:variable name="earlier" select="../@*[position() &lt; $pos]"/>
      <td>
        <xsl:value-of select="sum($earlier) + ."/>
      </td>
      <td>
        <xsl:for-each select="$earlier">
          <xsl:value-of select="name()"/> +
        </xsl:for-each>
        <xsl:value-of select="name()"/>
      </td>
    </tr>
  </xsl:for-each>
</table>

1

你可能能够拼凑出一个看起来可行的解决方案,但存在一个固有问题,你的解决方案将会崩溃。

问题在于XML中的属性没有显著的顺序。你不能依赖于属性以任何进程的方式呈现,无论是在XSLT内部还是外部,它们都不会按照它们在文本中出现的顺序呈现。坦白地说,我很惊讶XSLT甚至允许你在属性谓词中使用position()

(顺便说一下,这就是身份转换使用那个奇怪模式select="node()|@*"的原因。需要@*,因为node()不匹配属性。 node()不匹配属性,因为属性不是节点。节点有位置,而属性没有。)

如果你的应用程序依赖于属性的排序,那么它是有问题的,你需要重新设计它。

然而,如果你可以依靠属性名称提供某种排序方式,就像你的示例所示,那么就有一种方法可以解决这个问题:

<xsl:variable name="atts" select="@*"/>
<xsl:for-each select="$atts">
  <xsl:sort select="substring-after(name(), 'Node')" data-type="number"/>
  <td>
    <xsl:variable name="this" select="number(substring-after(name(), 'Node'))"/>
    <xsl:value-of select="sum($atts[
                  number(substring-after(name(), 'Node')) &lt;= $this])"/>
  </td>
</xsl:for-each>

这样做很丑陋吗?当然。并且,如果您稍微更改属性命名方案,它将会出错。但不管属性的顺序如何,它都能起作用。


谢谢Robert Rossney,这对我也很重要。 - Kedar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接