Aggregate-function for sum and product in XPath

前端 未结 3 1216
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-21 05:03

Similar to this question (http://stackoverflow.com/q/1333558/948404) I want to use XPath to calculate a sum over products in a structure like this:

         


        
相关标签:
3条回答
  • 2021-01-21 05:21

    Use this XPath 2.0 expression:

    sum(/items/item/(value * quantity))
    

    Here is an XSLT 2.0 transformation as verification:

    <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(/items/item/(value * quantity))"/>
        </xsl:template>
    </xsl:stylesheet>
    

    When this transformation is applied on the provided XML document:

    <items>
        <item>
            <value>1.0</value>
            <quantity>3</quantity>
        </item>
        <item>
            <value>2.5</value>
            <quantity>2</quantity>
        </item>
        <!-- ... -->
    </items>
    

    the XPath expression is evaluated and the result of this evaluation is output:

    8
    

    Explanation:

    In XPath 2.0 it is legal for a location step to be

    /(expression),

    or even

    /someFunction(argumentList)


    II. XSLT 1.0 solution:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/*">
      <xsl:call-template name="sumProducts">
        <xsl:with-param name="pNodes" select="item"/>
      </xsl:call-template>
     </xsl:template>
    
     <xsl:template name="sumProducts">
       <xsl:param name="pNodes" select="/.."/>
       <xsl:param name="pAccum" select="0"/>
    
       <xsl:choose>
        <xsl:when test="not($pNodes)">
         <xsl:value-of select="$pAccum"/>
        </xsl:when>
        <xsl:otherwise>
         <xsl:call-template name="sumProducts">
          <xsl:with-param name="pNodes" select="$pNodes[position() >1]"/>
          <xsl:with-param name="pAccum" select=
          "$pAccum + $pNodes[1]/value * $pNodes[1]/quantity"/>
         </xsl:call-template>
        </xsl:otherwise>
       </xsl:choose>
     </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on the provided XML document (above), again the wanted, correct result is produced:

    8
    

    Do note: Such kind of problems are easy to solve using the FXSL library. The template to call is transform-and-sum .

    0 讨论(0)
  • 2021-01-21 05:25

    Try this:

    <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(/items/item/(value * quantity))"/>
        </xsl:template>
    </xsl:stylesheet>
    
    0 讨论(0)
  • 2021-01-21 05:25

    Based on this suggestion https://stackoverflow.com/a/1334165/948404 I came up with a solution compliant with XSLT 1.0 and XPath 1.0, which are implemented by libxml/libxslt, used by PHPs implementation of XSLTProcessor (thanks to @Dimitre Novatchev for confirmation).

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:template match="/items">
            <xsl:call-template name="recursivesum">
                <xsl:with-param name="items" select="*" />
            </xsl:call-template>
        </xsl:template>
    
        <xsl:template name="recursivesum">
            <xsl:param name="items" />
            <xsl:param name="sum" select="0" />
            <xsl:variable name="head" select="$items[1]" />
            <xsl:variable name="tail" select="$items[position()>1]" />
            <xsl:variable name="thissum" select="$head/value * $head/quantity" />
            <xsl:choose>
                <xsl:when test="not($tail)">
                    <xsl:value-of select="$sum+$thissum" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="recursivesum">
                        <xsl:with-param name="sum" select="$sum+$thissum" />
                        <xsl:with-param name="items" select="$tail" />
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
    </xsl:transform>
    
    0 讨论(0)
提交回复
热议问题