代码之家  ›  专栏  ›  技术社区  ›  Dave

两个数相乘,然后求和

  •  10
  • Dave  · 技术社区  · 15 年前

    <xsl:value-of select="sum(Parts/Part/Quantity * Parts/Part/Rate)"/>
    

    此代码会导致错误,错误说明“函数sum的参数1无法转换为节点集。”

    2 回复  |  直到 5 年前
        1
  •  40
  •   Dimitre Novatchev    15 年前

    这里有三种可能的解决方案

    解决方案1 XSLT2:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="text"/>
    
        <xsl:template match="/">
          <xsl:sequence select="sum(/*/*/(rate * quantity))"/>
        </xsl:template>
    </xsl:stylesheet>
    

    当对该文档应用以下转换时

    <parts>
      <part>
            <rate>0.37</rate>
        <quantity>10</quantity>
      </part>
      <part>
            <rate>0.03</rate>
        <quantity>10</quantity>
      </part>
    </parts>
    

    想要的结果就产生了 :

    4

    XSLT 2.0 解决方案使用的事实是 XPath 2.0 允许最后一个“/”运算符的右参数可以是表达式,也可以是函数。此表达式/函数应用于迄今为止作为上下文节点选择的每个节点,并且每个函数应用程序生成一个结果。

    解决方案2 XSLT 1.0:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="text"/>
    
        <xsl:template match="/">
          <xsl:call-template name="sumProducts">
            <xsl:with-param name="pList" select="*/*"/>
          </xsl:call-template>
        </xsl:template>
    
        <xsl:template name="sumProducts">
            <xsl:param name="pList"/>
            <xsl:param name="pAccum" select="0"/>
    
            <xsl:choose>
              <xsl:when test="$pList">
                <xsl:variable name="vHead" select="$pList[1]"/>
    
                <xsl:call-template name="sumProducts">
                  <xsl:with-param name="pList" select="$pList[position() > 1]"/>
                  <xsl:with-param name="pAccum"
                   select="$pAccum + $vHead/rate * $vHead/quantity"/>
                </xsl:call-template>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="$pAccum"/>
              </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    </xsl:stylesheet>
    

    :

    4

    这是典型的XSLT1.0递归解决方案 sumProducts 模板递归地调用自身 ,直到整个输入列表传入参数 $pList

    解决方案3 FXSL(XSLT 1.0):

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ext="http://exslt.org/common"
    xmlns:test-map-product="test-map-product"
    exclude-result-prefixes="xsl ext test-map-product"
    >
       <xsl:import href="sum.xsl"/>
       <xsl:import href="map.xsl"/>
       <xsl:import href="product.xsl"/>
    
       <!-- This transformation is to be applied on:
            salesMap.xml
    
            It contains the code of the "sum of products" from the 
            article "The Functional Programming Language XSLT"
         -->
    
       <test-map-product:test-map-product/>
    
       <xsl:output method="text"/>
    
       <xsl:template match="/">
         <!-- Get: map product /sales/sale -->
         <xsl:variable name="vSalesTotals">
             <xsl:variable name="vTestMap" select="document('')/*/test-map-product:*[1]"/>
             <xsl:call-template name="map">
               <xsl:with-param name="pFun" select="$vTestMap"/>
               <xsl:with-param name="pList1" select="/sales/sale"/>
             </xsl:call-template>
         </xsl:variable>
    
         <!-- Get sum map product /sales/sale -->
          <xsl:call-template name="sum">
            <xsl:with-param name="pList" select="ext:node-set($vSalesTotals)/*"/>
          </xsl:call-template>
       </xsl:template>
    
        <xsl:template name="makeproduct" match="*[namespace-uri() = 'test-map-product']">
          <xsl:param name="arg1"/>
    
          <xsl:call-template name="product">
            <xsl:with-param name="pList" select="$arg1/*"/>
          </xsl:call-template>
        </xsl:template>
    </xsl:stylesheet>
    

    当对该文档应用以下转换时

    <sales>
      <sale>
        <price>3.5</price>
        <quantity>2</quantity>
        <Discount>0.75</Discount>
        <Discount>0.80</Discount>
        <Discount>0.90</Discount>
      </sale>
      <sale>
        <price>3.5</price>
        <quantity>2</quantity>
        <Discount>0.75</Discount>
        <Discount>0.80</Discount>
        <Discount>0.90</Discount>
      </sale>
    </sales>
    

    产生正确的结果

    7.5600000000000005

    在最后一种情况下 sale 我们计算 price , quantity discount -美国。

    FXSL 是高阶函数的纯XSLT实现。在这个例子中,高阶函数 f:map() 使用 映射函数 f:product() 销售

        2
  •  1
  •   cddr    15 年前

    迪米特里所有的解决方案都有效,而你不这么做是对的 需要

    如果您想坚持所选的解决方案,则需要使用Saxon或其他支持XSLT 2的XSLT处理器。

    否则,这里有一种在XSLT 1中实现的替代方法。这将在大多数XSLT处理器中工作,有些人可能会发现它比递归版本更容易摸索。就个人而言,我更喜欢递归版本(Dimitre的第三个建议),因为它更易于移植。

    <xsl:stylesheet version="1.0"
                    xmlns:ex="http://exslt.org/common"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="text"/>
    
      <xsl:template name="GetProducts">
        <xsl:param name="left"/>
        <xsl:param name="right"/>
    
        <xsl:for-each select="$left/text()">
          <product>
            <xsl:value-of select="number(.) * number($right[position()])"/>
          </product>
        </xsl:for-each>
      </xsl:template>
    
      <xsl:template match="/">
        <xsl:variable name="products">
          <xsl:call-template name="GetProducts">
            <xsl:with-param name="left" select="Parts/Part/Rate"/>
            <xsl:with-param name="right" select="Parts/Part/Quantity"/>
          </xsl:call-template>
        </xsl:variable>
    
        <xsl:value-of select="sum(ex:node-set($products)/product)"/>
      </xsl:template>
    </xsl:stylesheet>