在xsl中的with-param中使用时,算术运算不起作用

时间:2013-11-13 03:03:56

标签: xslt

有人可以解释为什么以下情况不起作用。

这是xml:

<?xml version="1.0" ?>
<testsuites>
    <testsuite errors="0" failures="0" name="test_ui_orchestration" tests="10" time="90.190"/>
    <testsuite errors="1" failures="0" name="test_ui_tables" tests="13" time="1115.771"/>
    <testsuite errors="0" failures="3" name="test_ui_dashboard" tests="18" time="116.397"/>
</testsuites>

我想计算总测试次数,总次数和失败次数。我有一个问题是获得失败总数(失败+错误)和总传递次数(为简单起见:测试 - 失败)。我调用相同的xml-template来计算使用“with-param”传递不同值的总数,如下所示(“计算总和”注释中的部分):

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/testsuites">
  <html>
  <body>

  <!-- Calculate sums start -->
  <xsl:variable name="allSum">
    <xsl:call-template name="testsSum">
      <xsl:with-param name="numTests" select="testsuite/@tests"/>
    </xsl:call-template>
  </xsl:variable>
    <xsl:variable name="failedSum">
      <xsl:call-template name="testsSum">
        <xsl:with-param name="numTests" select="testsuite/@failures"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="errorSum">
      <xsl:call-template name="testsSum">
        <xsl:with-param name="numTests" select="testsuite/@errors"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="failederrorSum">
      <xsl:call-template name="testsSum">
        <xsl:with-param name="numTests" select="testsuite/@failures + testsuite/@errors"/>
      </xsl:call-template>
    </xsl:variable>
  <!-- Calculate sums ends -->

  <h2>Test Results</h2>
  <table border="1">
    <tr bgcolor="#9acd32">
      <th>Test Area</th>
      <th>Total</th>
      <th>Pass</th>
      <th>Fail</th>
      <th>Error</th>
      <th>Fail and Error</th>
    </tr>

    <xsl:for-each select="testsuite">
          <tr>
              <td><xsl:value-of select="@name"/></td>
              <td><xsl:value-of select="@tests"/></td>
              <td><xsl:value-of select="@tests - (@failures + @errors)"/></td>
              <td><xsl:value-of select="@failures"/></td>
              <td><xsl:value-of select="@errors"/></td>
              <td><xsl:value-of select="@failures + @errors"/></td>
              <td> </td>
          </tr>
    </xsl:for-each>

    <tr>
      <td><b>All Tests</b></td>
      <td><xsl:value-of select="$allSum"/></td>
      <td>foo</td>
        <!--
      <td><xsl:value-of select="$passedSum"/></td>
      -->
      <td><xsl:value-of select="$failedSum"/></td>
      <td><xsl:value-of select="$errorSum"/></td>
      <td><xsl:value-of select="$failederrorSum"/></td>
      <td></td>
    </tr>

  </table>
  </body>
  </html>
</xsl:template>

<xsl:template name="testsSum">
  <xsl:param name="numTests"/>
    <xsl:choose>
      <xsl:when test="$numTests">
        <xsl:variable name="recursive_result">
          <xsl:call-template name="testsSum">
            <xsl:with-param name="numTests" select="$numTests[position() > 1]"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="number($numTests[1]) + $recursive_result"/>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

这会为总和产生以下输出(完整输出在本文结尾处):

41 3 1 0

虽然它应该是:

41 3 1 4

每个单独的和工作正常,找到相应的属性。但是,当我尝试添加两个属性并将其传递给sum模板时,它无法按预期工作。

情况变得更糟。生成总测试的代码是:

<xsl:variable name="passedSum">
    <xsl:call-template name="testsSum">
        <xsl:with-param name="numTests"
                select="testsuite/@tests - testsuite/@failures"/>
    </xsl:call-template>
</xsl:variable>

启用上述代码后,出现以下错误:

Failed to evaluate the expression of variable 'numTests'

我一直在网上搜索并了解“with-param”的“select”接受“定义参数值的XPath表达式”(来自w3schools)。 XPath表达式可以包含算术运算(http://www.w3schools.com/xpath/xpath_operators.asp),那么为什么上面的失败呢?

这是xsl转换的完整输出:

Test Area               Total   Pass    Fail    Error   Fail and Error
----------------------------------------------------------------------
test_ui_orchestration   10      10      0       0       0
test_ui_tables          13      12      0       1       1   
test_ui_dashboard       18      15      3       0       3
----------------------------------------------------------------------  
All Tests               41      foo     3       1       0

以下是xsl转换的预期输出(标有*的修改值):

Test Area               Total   Pass    Fail    Error   Fail and Error
----------------------------------------------------------------------
test_ui_orchestration   10      10      0       0       0
test_ui_tables          13      12      0       1       1   
test_ui_dashboard       18      15      3       0       3
----------------------------------------------------------------------  
All Tests               41      37*     3       1       4*

3 个答案:

答案 0 :(得分:2)

正如Stormtroopr所说,你没有向我们展示设置上下文的XSLT代码部分,而你遗漏了这一事实表明你还没有理解上下文在XSLT中的重要性。如果您的XPath表达式完全选择任何内容,我们可以假设上下文项是testsuites元素,在这种情况下,路径表达式(如testsuite/@failures)都选择多个值(包含多个属性节点的节点集)。如果您调用的模板需要节点集,那就没问题。但是当您使用包含多个节点的节点集作为算术运算的输入时,则:

(a)在XSLT 1.0中,它使用节点集中的第一个节点而忽略其他节点

(b)在XSLT 2.0中,您会收到一个类型错误,说明您正在做的事情毫无意义。

由于您收到错误,我假设您使用的是XSLT 2.0,但它似乎不是一个非常有用的错误消息(您使用的是哪个XSLT处理器?)

至于解决问题,我无法解决这个问题,因为你没有解释你想要实现的目标 - 我无法对错误代码的要求进行逆向工程。

答案 1 :(得分:0)

不幸的是,您只发布了XSLT的片段,但我认为问题在于您是从testsuite/@failures元素调用testsuites

而不是返回一个数字,而是返回结果树片段或节点集,其中包含每个failures元素的testsuite属性的值。

因此传统的数字操作失败了,因为他们期望一个数字并获得一个RTF或节点集,并尝试应用操作产生意外结果。

在不知道代码的其余部分是什么的情况下,我建议修复的方法是将变量实例化包装在for-each循环中,甚至更好地包装在与个人匹配的模板中{ {1}}。

答案 2 :(得分:0)

由于问题发生了巨大变化,我已经做出了新的答案。

您的求和模板(实际上大部分模板)都可以正常工作。所有你需要做的就是将这些总数降到最低点,你只需要对你已经创建的变量进行加/减:

<xsl:variable name="failederrorSum" select="$failedSum + $errorSum" />
<xsl:variable name="passedSum" select="$allSum - $failederrorSum" />

此样式表应用于输入XML:

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

<xsl:template match="/testsuites">
  <html>
  <body>

  <!-- Calculate sums start -->
  <xsl:variable name="allSum">
    <xsl:call-template name="testsSum">
      <xsl:with-param name="numTests" select="testsuite/@tests"/>
    </xsl:call-template>
  </xsl:variable>
    <xsl:variable name="failedSum">
      <xsl:call-template name="testsSum">
        <xsl:with-param name="numTests" select="testsuite/@failures"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="errorSum">
      <xsl:call-template name="testsSum">
        <xsl:with-param name="numTests" select="testsuite/@errors"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="failederrorSum" select="$failedSum + $errorSum" />
    <xsl:variable name="passedSum" select="$allSum - $failederrorSum" />
  <!-- Calculate sums ends -->

  <h2>Test Results</h2>
  <table border="1">
    <tr bgcolor="#9acd32">
      <th>Test Area</th>
      <th>Total</th>
      <th>Pass</th>
      <th>Fail</th>
      <th>Error</th>
      <th>Fail and Error</th>
    </tr>

    <xsl:apply-templates />

    <tr>
      <td><b>All Tests</b></td>
      <td><xsl:value-of select="$allSum"/></td>
      <td><xsl:value-of select="$passedSum"/></td>
      <td><xsl:value-of select="$failedSum"/></td>
      <td><xsl:value-of select="$errorSum"/></td>
      <td><xsl:value-of select="$failederrorSum"/></td>
      <td></td>
    </tr>

  </table>
  </body>
  </html>
</xsl:template>


<xsl:template match="testsuite">
          <tr>
              <td><xsl:value-of select="@name"/></td>
              <td><xsl:value-of select="@tests"/></td>
              <td><xsl:value-of select="@tests - (@failures + @errors)"/></td>
              <td><xsl:value-of select="@failures"/></td>
              <td><xsl:value-of select="@errors"/></td>
              <td><xsl:value-of select="@failures + @errors"/></td>
              <td> </td>
          </tr>
</xsl:template>

<xsl:template name="testsSum">
  <xsl:param name="numTests"/>
    <xsl:choose>
      <xsl:when test="$numTests">
        <xsl:variable name="recursive_result">
          <xsl:call-template name="testsSum">
            <xsl:with-param name="numTests" select="$numTests[position() > 1]"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="number($numTests[1]) + $recursive_result"/>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

给出此输出:

<html>
    <body>
        <h2>Test Results</h2>
        <table border="1">
            <tr bgcolor="#9acd32">
                <th>Test Area</th>
                <th>Total</th>
                <th>Pass</th>
                <th>Fail</th>
                <th>Error</th>
                <th>Fail and Error</th>
            </tr>
            <tr>
                <td>test_ui_orchestration</td>
                <td>10</td>
                <td>10</td>
                <td>0</td>
                <td>0</td>
                <td>0</td>
                <td>
                </td>
            </tr>
            <tr>
                <td>test_ui_tables</td>
                <td>13</td>
                <td>12</td>
                <td>0</td>
                <td>1</td>
                <td>1</td>
                <td>
                </td>
            </tr>
            <tr>
                <td>test_ui_dashboard</td>
                <td>18</td>
                <td>15</td>
                <td>3</td>
                <td>0</td>
                <td>3</td>
                <td>
                </td>
            </tr>
            <tr>
                <td>
                    <b>All Tests</b>
                </td>
                <td>41</td>
                <td>37</td>
                <td>3</td>
                <td>1</td>
                <td>4</td>
                <td/>
            </tr>
        </table>
    </body>
</html>

看起来像这样:

Test results screenshot