How to get the number of rows and SUM of the fields after grouping them in XSLT

为君一笑 提交于 2021-02-11 08:42:26

问题


I have an XML that contains workers and amounts. In the output, the file will contain lines for a worker - one line if he has EX and one line if he has ER. He has EX if he has amount1 in the xml file, and ER if he has amount2. This works perfectly fine in the my current XSLT.

The main issue is on how to properly get the correct total number of rows and the sum of all amounts (both amount1 and amount2) after grouping them, and should be added in the header.

Here's the sample XML:

<Report_Data>
<Report_Entry>
    <Worker>
        <Name>User1</Name>            
        <ID>1234</ID>
    </Worker>
    <amount1>-200</amount1>
    <amount2>100</amount2> 
</Report_Entry>    
<Report_Entry>
    <Worker>
        <Name>User1</Name>           
        <ID>1234</ID>
    </Worker>
    <amount1>100</amount1>
    <amount2>-50</amount2> 
</Report_Entry>
<Report_Entry>
    <Worker>
        <Name>User2</Name>            
        <ID>5678</ID>
    </Worker>
    <amount1>-20</amount1>  
    <amount2>100</amount2>         
</Report_Entry>
<Report_Entry>
    <Worker>
        <Name>User1</Name>            
        <ID>1234</ID>
    </Worker>
    <amount1>0</amount1>
    <amount2>0</amount2> 
</Report_Entry>  
</Report_Data>


Here's my sample XSLT (im working on the counter but cant get it right. Tried a lot but I ended up having the count in the variable here but it's getting all the rows instead of 3. i think its because of the current-group looking all the row values on the xml...) :

<?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"
xmlns:saxon="http://saxon.sf.net/" xmlns:ns0="urn:com.foo" extension-element-prefixes="saxon"
version="2.0">

<xsl:output method="text"/>


<xsl:variable name="SumOfAmount1" saxon:assignable="yes"/>
<xsl:variable name="SumOfAmount2" saxon:assignable="yes"/>

<xsl:variable name="countEX" as="xs:string*">
    <xsl:for-each-group select="Report_Data/Report_Entry" group-by="Worker/ID">

        <saxon:assign name="SumOfAmount1" select="sum(current-group()/Report_Data/Report_Entry/amount1)"/>            
        <xsl:if test="$SumOfAmount1 >= 0.00">
            <xsl:sequence select="current-group()"></xsl:sequence>

        </xsl:if>     

    </xsl:for-each-group>          
</xsl:variable>
<xsl:variable name="countER" as="xs:string*">
    <xsl:for-each-group select="/Report_Data/wd:Report_Entry" group-by="ID">

        <saxon:assign name="SumOfAmount1" select="sum(current-group()/Report_Data/Report_Entry/amount2)"/>            
        <xsl:if test="$SumOfAmount1 >= 0.00">
            <xsl:sequence select="current-group()"></xsl:sequence>                
        </xsl:if>     

    </xsl:for-each-group>          
</xsl:variable>


<xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Report_Data">        

    <Header>
        <!--Total count of rows!-->
         //add countER and countEX
        <xsl:text>,</xsl:text>
        <!--Sum of amount1 and amount2 !-->
        //add total amounts 

    </Header>
    <ReportData>

        <xsl:for-each-group select="Report_Entry" group-by="Worker/ID">
            <ReportEntry>                                            
                <xsl:copy-of select="current-group()[1]/*[starts-with(name(), 'ID')]"/>
            </ReportEntry>

            <saxon:assign name="SumOfAmount1" select="sum(current-group()/amount1)"/>
            <saxon:assign name="SumOfAmount2" select="sum(current-group()/amount2)"/>


            <xsl:if test="$SumOfAmount1 >= 0.00">
                <xsl:call-template name="EX"></xsl:call-template>                    
            </xsl:if>
            <xsl:if test="$SumOfAmount2 >= 0.00">                                       
                <xsl:call-template name="ER"></xsl:call-template>                                                               
            </xsl:if>                                   

        </xsl:for-each-group>

    </ReportData>

</xsl:template>

<xsl:template name="EX">              
    <Name>
        <xsl:value-of select="Worker/Name"/>
    </Name>
    <xsl:text>,</xsl:text>
    <ID>
        <xsl:value-of select="Worker/ID"/>
    </ID>
    <xsl:text>,</xsl:text>        
    <amount1>            
        <xsl:value-of select="$SumOfAmount1"/>            
    </amount1>
    <xsl:text>,</xsl:text>                                                       
    <xsl:value-of select="'EX'"></xsl:value-of>        
    <xsl:value-of select="'&#xa;'"></xsl:value-of>            
</xsl:template>

<xsl:template name="ER">         
    <Name>
        <xsl:value-of select="Worker/Name"/>
    </Name>
    <xsl:text>,</xsl:text>
    <ID>
        <xsl:value-of select="Worker/ID"/>
    </ID>
    <xsl:text>,</xsl:text>        
    <amount1>            
        <xsl:value-of select="$SumOfAmount2"/>            
    </amount1>
    <xsl:text>,</xsl:text>                                                       
    <xsl:value-of select="'ER'"></xsl:value-of>        
    <xsl:value-of select="'&#xa;'"></xsl:value-of>                                                                                            
</xsl:template>

</xsl:stylesheet>

The expected output:

 2,150
 User1,1234,50,ER
 User2,5678,100,ER

where: 2 is the number of rows and 150 is the sum of all the amounts (amount1 and amount2).

P.S. negative amounts are not included in the file. if amount1 or amount2 are negative, they are still included in the addition but if the sum amounts to negative, it will be excluded and not displayed in the file.

Thanks for the help.


回答1:


I would group once by the ID and then store the row data based on whether there are the amount1/amount2 elements. To compute the sum I would simply select the elements again:

  <xsl:template match="/">
      <xsl:variable name="rows" as="element(row)*">
         <xsl:for-each-group select="Report_Data/Report_Entry" group-by="Worker/ID">
             <xsl:if test="current-group()/amount1">
                 <row>
                     <xsl:value-of select="Worker/Name, current-grouping-key(), sum(current-group()/amount1), 'EX'" separator=","/>
                 </row>
             </xsl:if>
             <xsl:if test="current-group()/amount2">
                 <row>
                     <xsl:value-of select="Worker/Name, current-grouping-key(), sum(current-group()/amount2), 'ER'" separator=","/>
                 </row>
             </xsl:if>
        </xsl:for-each-group>
      </xsl:variable>
      <xsl:value-of select="count($rows), sum(Report_Data/Report_Entry/*[starts-with(local-name(), 'amount')]), $rows" separator="&#10;"/>
  </xsl:template>

http://xsltfiddle.liberty-development.net/bdxtpA

It seems I did not quite get the format right, see http://xsltfiddle.liberty-development.net/bdxtpA/1 for the correction which uses

  <xsl:value-of select="concat(count($rows), ',', sum(Report_Data/Report_Entry/*[starts-with(local-name(), 'amount')])), $rows" separator="&#10;"/>

For the refined requirement to eliminate groups with summed amounts being negative I think that

  <xsl:template match="/">
      <xsl:variable name="rows" as="element(row)*">
         <xsl:for-each-group select="Report_Data/Report_Entry" group-by="Worker/ID">
             <xsl:variable name="sum1" select="sum(current-group()/amount1)"/>
             <xsl:if test="current-group()/amount1 and $sum1 > 0">
                 <row sum="{$sum1}">
                     <xsl:value-of select="Worker/Name, current-grouping-key(), $sum1, 'EX'" separator=","/>
                 </row>
             </xsl:if>
             <xsl:variable name="sum2" select="sum(current-group()/amount2)"/>
             <xsl:if test="current-group()/amount2 and $sum2 > 0">
                 <row sum="{$sum2}">
                     <xsl:value-of select="Worker/Name, current-grouping-key(), $sum2, 'ER'" separator=","/>
                 </row>
             </xsl:if>
        </xsl:for-each-group>
      </xsl:variable>
      <xsl:value-of select="concat(count($rows), ',', sum($rows/@sum)), $rows" separator="&#10;"/>
  </xsl:template>

does that: http://xsltfiddle.liberty-development.net/bdxtpA/2



来源:https://stackoverflow.com/questions/49534790/how-to-get-the-number-of-rows-and-sum-of-the-fields-after-grouping-them-in-xslt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!