1
votes

I would like to convert below XML through XSLT by groping based on Email tag and merge two nodes into one and get all the elements in one node. Please advise

Input XML:

<?xml version="1.0" encoding="UTF-8"?>
<Details>  
    <Worker>
        <Name>AAA</Name>
        <Email>[email protected]</Email>
        <Item>BBB</Item>
        <Avg_Amount>425.20</Avg_Amount>
    </Worker>
    <Worker>
        <Manager>AAA</Manager>
        <Email>[email protected]</Email>
        <Item>BBB</Item>
        <Draft_Amount>1826.64</Draft_Amount>
    </Worker>   
</Details>

XSLT:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:wd="urn:com.test/bsvc" exclude-result-prefixes="wd">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    
    <xsl:template match="/">
        
        <Transactions>
            
            <xsl:for-each-group select="Details/Worker" group-by='Email'>
                
                <Manager>             
                    <Name>
                        <xsl:value-of select="Name"/>
                    </Name>
                    <Email>
                        <xsl:value-of select="Email"/>
                    </Email>
                    
                    
                    <xsl:for-each-group select="current-group()" group-by="Item">
                        
                        <Worker>
                            <Name>
                                <xsl:value-of select="Name"/>
                            </Name>
                            <Email>
                                <xsl:value-of select="Email"/>
                            </Email>
                            <Item>
                                <xsl:value-of select="Item"/>
                            </Item>
                            
                            <Avg_Amount>
                                <xsl:value-of select="Avg_Amount"/>
                            </Avg_Amount>
                            <Draft_Amount>
                                <xsl:value-of select="Charges_Draft"/>
                            </Draft_Amount>
                            
                            
                        </Worker>
                        
                        
                        
                    </xsl:for-each-group>
                    
                </Manager>
                
            </xsl:for-each-group>
            
        </Transactions>
    </xsl:template>
</xsl:stylesheet>

Output generated:

<Transactions>
   <Manager>
      <Name>AAA</Name>
      <Email>[email protected]</Email>
      <Worker>
         <Name>AAA</Name>
         <Email>[email protected]</Email>
         <Item>BBB</Item>
         <Avg_Amount>425.20</Avg_Amount>
         <Draft_Amount/>
      </Worker>
   </Manager>
</Transactions>

Desired Output:

<Transactions>
   <Manager>
      <Name>AAA</Name>
      <Email>[email protected]</Email>
      <Worker>
         <Name>AAA</Name>
         <Email>[email protected]</Email>
         <Item>BBB</Item>
         <Avg_Amount>425.20</Avg_Amount>
         <Draft_Amount>1826.64</Draft_Amount>
      </Worker>
   </Manager>
</Transactions>

I would like to convert below XML through XSLT by groping based on Email tag and merge two nodes into one and get all the elements in one node. Please advise

1
Will Name and Item always be the same between the two elements sharing the same e-mail? - Sebastien
Change <xsl:value-of select="Charges_Draft"/> to <xsl:value-of select="Draft_Amount"/>. - zx485

1 Answers

1
votes

Change the Draft_Amount to select Draft_Amount from the current-group():

<Draft_Amount>
  <xsl:value-of select="current-group()/Draft_Amount"/>
</Draft_Amount>

But should consider what you would want to do if there are more than one Draft_Amount among multiple Worker. You could average them, select the first, etc.

Also, your example data only has one Avg_Amount element, but you might consider changing Avg_Amount to compute the average of all Avg_Amount.

<Avg_Amount>
  <xsl:value-of select="avg(current-group()/Avg_Amount)"/>
</Avg_Amount>

You could eliminate the extra xsl:for-each, and could simplify the code a bit and just copy some of those elements, instead of recreating the element and selecting the values:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:wd="urn:com.test/bsvc" exclude-result-prefixes="wd">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    
    <xsl:template match="/"> 
        <Transactions>
            <xsl:for-each-group select="Details/Worker" group-by='Email'>     
                <Manager>   
                    <xsl:copy-of select="Name|Email"/>
                    <Worker>
                        <xsl:copy-of select="Name|Email|Item"/>
                        <Avg_Amount>
                            <xsl:value-of select="avg(current-group()/Avg_Amount)"/>
                        </Avg_Amount>
                        <xsl:copy-of select="(current-group()/Draft_Amount)[1]"/>
                    </Worker>
                </Manager>
            </xsl:for-each-group>
        </Transactions>
    </xsl:template>
</xsl:stylesheet>