2009-08-26 81 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,你可以這樣做。