xmlxsltxslt-2.0xslt-grouping

XSLT : Sum and remove xml nodes based on conditions


I'm currently working on a tricky transformation in which I need to sum the amount of the game articles price value when the article appears twice and to exclude the second article from the output.

Explanation : Each article has a id, name, price and a copy number. In my case, I always have two copies. Copy 0 & Copy 1. If an article has two copies, then we add the price of article copy 1 in article copy 0 and we exclude copy 1 from the output

I'm not exactly sure on how to approach this requirement.

The XML input

<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data>
  <wd:Report_Entry>
    <wd:id>1000</wd:id>
    <wd:name>Call of Duty</wd:name>
    <wd:price>50.12</wd:price>
    <wd:copy>0</wd:copy>
  </wd:Report_Entry>
  <wd:Report_Entry>
    <wd:id>1000</wd:id>
    <wd:name>Call of Duty</wd:name>
    <wd:price>60.0</wd:price>
    <wd:copy>1</wd:copy>
  </wd:Report_Entry>
  <wd:Report_Entry>
    <wd:id>2000</wd:id>
    <wd:name>Metal Slug</wd:name>
    <wd:price>75.00</wd:price>
    <wd:copy>0</wd:copy>
  </wd:Report_Entry>
  <wd:Report_Entry>
    <wd:id>3000</wd:id>
    <wd:name>Need For Speed</wd:name>
    <wd:price>40.00</wd:price>
    <wd:copy>0</wd:copy>
  </wd:Report_Entry>
</wd:Report_Data>

The expected XML output:

<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data>
  <wd:Report_Entry>
    <wd:id>1000</wd:id>
    <wd:name>Call of Duty</wd:name>
    <wd:price>110.12</wd:price>
    <wd:copy>0</wd:copy>
  </wd:Report_Entry>
  <wd:Report_Entry>
    <wd:id>2000</wd:id>
    <wd:name>Metal Slug</wd:name>
    <wd:price>75.00</wd:price>
    <wd:copy>0</wd:copy>
  </wd:Report_Entry>
  <wd:Report_Entry>
    <wd:id>3000</wd:id>
    <wd:name>Need For Speed</wd:name>
    <wd:price>40.00</wd:price>
    <wd:copy>0</wd:copy>
  </wd:Report_Entry>
</wd:Report_Data>

Following @michael.hor257k recommendation, I was able to create a grouping structure and sum the price. However, I still don't get the expected result

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:wd="http://www.workday.org/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:template match="/">
            <xsl:for-each-group select="wd:Report_Data/wd:Report_Entry" group-by="wd:id">
                <xsl:copy>
                    <xsl:copy-of select="wd:id"/>
                    <xsl:copy-of select="wd:name"/>
                    <wd:price>
                        <xsl:value-of select="sum(current-group()/wd:price)" />
                    </wd:price>                 
                    <xsl:copy-of select="wd:copy"/>
                </xsl:copy>
            </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

Result (not what I want )

<wd:Report_Entry xmlns:wd="http://www.workday.org/">
   <wd:id>1000</wd:id>
   <wd:name>Call of Duty</wd:name>
   <wd:price xmlns:xs="http://www.w3.org/2001/XMLSchema">110.12</wd:price>
   <wd:copy>0</wd:copy>
</wd:Report_Entry>
<wd:Report_Entry xmlns:wd="http://www.workday.org/">
   <wd:id>2000</wd:id>
   <wd:name>Metal Slug</wd:name>
   <wd:price xmlns:xs="http://www.w3.org/2001/XMLSchema">75</wd:price>
   <wd:copy>0</wd:copy>
</wd:Report_Entry>
<wd:Report_Entry xmlns:wd="http://www.workday.org/">
   <wd:id>3000</wd:id>
   <wd:name>Need For Speed</wd:name>
   <wd:price xmlns:xs="http://www.w3.org/2001/XMLSchema">40</wd:price>
   <wd:copy>0</wd:copy>
</wd:Report_Entry>

Solution

  • You only need to make a few small changes:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:wd="http://www.workday.org/">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="/wd:Report_Data">
        <xsl:copy>
            <xsl:for-each-group select="wd:Report_Entry" group-by="wd:id">
                <xsl:copy>
                    <xsl:copy-of select="wd:id"/>
                    <xsl:copy-of select="wd:name"/>
                    <wd:price>
                        <xsl:value-of select="sum(current-group()/wd:price)" />
                    </wd:price>                 
                    <xsl:copy-of select="wd:copy"/>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>