2009-08-26 222 views
6

的产品,我有以下XML源结构:XSLT总结两个属性

<turnovers> 
    <turnover repid="1" amount="500" rate="0.1"/> 
    <turnover repid="5" amount="600" rate="0.5"/> 
    <turnover repid="4" amount="400" rate="0.2"/> 
    <turnover repid="1" amount="700" rate="0.05"/> 
    <turnover repid="2" amount="100" rate="0.15"/> 
    <turnover repid="1" amount="900" rate="0.25"/> 
    <turnover repid="2" amount="1000" rate="0.18"/> 
    <turnover repid="5" amount="200" rate="0.55"/> 
    <turnover repid="9" amount="700" rate="0.40"/> 
</turnovers> 

我需要一个XSL:价值的select语句,将返回速率属性和数量的乘积的总和属性给定的代表ID。所以对于代表5我需要((600 x 0.5)+(200 x 0.55))。

回答

8
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <xsl:template match="/turnovers"> 
    <val> 
     <!-- call the sum function (with the relevant nodes) --> 
     <xsl:call-template name="sum"> 
     <xsl:with-param name="nodes" select="turnover[@repid='5']" /> 
     </xsl:call-template> 
    </val> 
    </xsl:template> 

    <xsl:template name="sum"> 
    <xsl:param name="nodes" /> 
    <xsl:param name="sum" select="0" /> 

    <xsl:variable name="curr" select="$nodes[1]" /> 

    <!-- if we have a node, calculate & recurse --> 
    <xsl:if test="$curr"> 
     <xsl:variable name="runningsum" select=" 
     $sum + $curr/@amount * $curr/@rate 
     " /> 
     <xsl:call-template name="sum"> 
     <xsl:with-param name="nodes" select="$nodes[position() &gt; 1]" /> 
     <xsl:with-param name="sum" select="$runningsum" /> 
     </xsl:call-template> 
    </xsl:if> 

    <!-- if we don't have a node (last recursive step), return sum --> 
    <xsl:if test="not($curr)"> 
     <xsl:value-of select="$sum" /> 
    </xsl:if> 

    </xsl:template> 
</xsl:stylesheet> 

给出:

<val>410</val> 

两个<xsl:if> s可以替换为单个<xsl:choose>。这意味着在递归过程中少检查一次,但它也意味着另外两行代码。

-2

做到在XSLT最简单的方法可能是使用的编程语言绑定,这样就可以定义自己的XPath函数。

1

这应该做的伎俩,你需要做一些进一步的工作来选择不同的repid

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

    <xsl:template match="/">  
     <xsl:variable name="totals"> 
      <product> 
       <xsl:for-each select="turnovers/turnover"> 
        <repid repid="{@repid}"> 
         <value><xsl:value-of select="@amount * @rate"/></value> 
        </repid> 
       </xsl:for-each> 
      </product> 
     </xsl:variable> 
     <totals> 
      <total repid="5" value="{sum($totals/product/repid[@repid='5']/value)}"/> 
     </totals>    
    </xsl:template> 

</xsl:stylesheet> 
+0

我不知道你可以创建全新的节点,将它分配给一个变量,然后查询它。整洁,谢谢约翰,将测试它。 – staterium 2009-08-26 11:49:27

+0

@Ravish:你不能,直到XSLT 2.0。在1.0中,你需要node-set()扩展函数。 – Tomalak 2009-08-26 12:09:16

2

在纯XSLT 1.0,你需要为这个递归模板,例如:

<xsl:template match="turnovers"> 
    <xsl:variable name="selectedId" select="5" /> 
    <xsl:call-template name="sum_turnover"> 
     <xsl:with-param name="turnovers" select="turnover[@repid=$selectedId]" /> 
    </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="sum_turnover"> 
    <xsl:param name="total" select="0" /> 
    <xsl:param name="turnovers" /> 
    <xsl:variable name="head" select="$turnovers[1]" /> 
    <xsl:variable name="tail" select="$turnovers[position()>1]" /> 
    <xsl:variable name="calc" select="$head/@amount * $head/@rate" /> 
    <xsl:choose> 
     <xsl:when test="not($tail)"> 
     <xsl:value-of select="$total + $calc" /> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:call-template name="sum_turnover"> 
      <xsl:with-param name="total" select="$total + $calc" /> 
      <xsl:with-param name="turnovers" select="$tail" /> 
     </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:template> 
+0

该解决方案以某种方式缺少“针对给定的@repid”部分。 – Tomalak 2009-08-26 12:12:41

+0

是的,我错过了@repid部分。这里是我的另一个版本 – jor 2009-08-26 12:45:48

+0

+1。干净的方法。 – Tomalak 2009-08-26 12:58:58

1

在XSLT 1.0使用FXSL使得这样的问题易于解决:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:f="http://fxsl.sf.net/" 
xmlns:ext="http://exslt.org/common" 
exclude-result-prefixes="xsl f ext" 
> 
<xsl:import href="zipWith.xsl"/> 
<xsl:output method="text"/> 

    <xsl:variable name="vMultFun" select="document('')/*/f:mult-func[1]"/>  

    <xsl:template match="/"> 
     <xsl:call-template name="profitForId"/> 
    </xsl:template> 

    <xsl:template name="profitForId"> 
     <xsl:param name="pId" select="1"/> 

     <xsl:variable name="vrtfProducts"> 
      <xsl:call-template name="zipWith"> 
      <xsl:with-param name="pFun" select="$vMultFun"/> 
      <xsl:with-param name="pList1" select="/*/*[@repid = $pId]/@amount"/> 
      <xsl:with-param name="pList2" select="/*/*[@repid = $pId]/@rate"/> 
      </xsl:call-template> 
     </xsl:variable> 

     <xsl:value-of select="sum(ext:node-set($vrtfProducts)/*)"/> 
    </xsl:template> 

    <f:mult-func/> 
    <xsl:template match="f:mult-func" mode="f:FXSL"> 
    <xsl:param name="pArg1"/> 
    <xsl:param name="pArg2"/> 

    <xsl:value-of select="$pArg1 * $pArg2"/> 
    </xsl:template> 
</xsl:stylesheet> 

当在最初发布源XML文档被应用于这种转变,正确的结果产生:

在XSLT 2.0中使用相同的解决方案使用FXSL 2.0可以通过一个XPath单行来表达:

sum(f:zipWith(f:multiply(), 
      /*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.), 
      /*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.) 
     ) 
    ) 

整个变换:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:f="http://fxsl.sf.net/" 
exclude-result-prefixes="f xs" 
> 
<xsl:import href="../f/func-zipWithDVC.xsl"/> 
<xsl:import href="../f/func-Operators.xsl"/> 

<!-- To be applied on testFunc-zipWith4.xml --> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/"> 
    <xsl:value-of select= 
    "sum(f:zipWith(f:multiply(), 
       /*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.), 
       /*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.) 
      ) 
     ) 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

同样,这变换产生了正确的答案:

请注意以下

  1. f:zipWith()函数采用作为自变量的函数fun()(两个参数),并具有相同长度的物品的两个列表。它会生成一个相同长度的新列表,其中的项目是fun()成对应用于两个列表中相应的k项目的结果。

  2. f:zipWith()如在表达式接收功能对应的“ammount”和“rate”的f:multiply()和两个序列属性。该sesult是一个序列,其中每个项目是相应的“ammount”和“rate”的产品。

  3. 最后,产生该序列的sum

  4. 没有必要写一个明确的递归和它也保证了内f:zipWith()使用的背后的幕后递归永远不会崩溃(适用于所有实际情况)与“堆栈溢出”

0
<?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="xs" 
     version="2.0"> 

    <xsl:variable name="repid" select="5" /> 

    <xsl:template match="/"> 
     <xsl:value-of select= 
     "sum(for $x in /turnovers/turnover[@repid=$repid] return $x/@amount * $x/@rate)"/> 
    </xsl:template> 

</xsl:stylesheet> 

如果你只是需要值而不是xml,你可以这样做。