Search code examples
xsltxslt-2.0

XSLT 2.0 : Group and Copy Nodes based on a condition


Here is my source XML

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <UniqueID>67896</UniqueID>
        <ExpenseAmount>400.00</ExpenseAmount>
        <BonusAmount>230.00</BonusAmount>
    </Worker>
    <Worker>
        <UniqueID>567899</UniqueID>
        <ExpenseAmount>190.00</ExpenseAmount>
        <BonusAmount>2000.00</BonusAmount>
    </Worker>
    <Worker>
        <UniqueID>12354</UniqueID>
        <Date>2023-10-06-07:00</Date>
        <ExpenseAmount>998.00</ExpenseAmount>
        <BonusAmount>400.00</BonusAmount>
    </Worker>
    <Worker>
        <UniqueID>12354</UniqueID>
        <Date>2023-10-10-07:00</Date>
        <ExpenseAmount>998.00</ExpenseAmount>
        <BonusAmount>400.00</BonusAmount>
    </Worker>    
    <Worker>
        <UniqueID>4567</UniqueID>
        <Date>2023-10-06-07:00</Date>
        <ExpenseAmount>1507.00</ExpenseAmount>
        <BonusAmount>322.00</BonusAmount>
    </Worker>
    <Worker>
        <UniqueID>4567</UniqueID>
        <Date>2023-10-10-07:00</Date>
        <ExpenseAmount>1507.00</ExpenseAmount>
        <BonusAmount>322.00</BonusAmount>
    </Worker>
</Workers>

Expected Output

Group the nodes based on unique <Date> value and copy all nodes that doesn't have <Date> element along with node that has unique <Date>

    <?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Records>
        <Worker> <!-- This node doesnt have <Date> element -->
            <UniqueID>67896</UniqueID> 
            <ExpenseAmount>400.00</ExpenseAmount>
            <BonusAmount>230.00</BonusAmount>
        </Worker>
        <Worker><!-- This node doesnt have <Date> element  -->
            <UniqueID>567899</UniqueID>
            <ExpenseAmount>190.00</ExpenseAmount>
            <BonusAmount>2000.00</BonusAmount>
        </Worker>
        <Worker>  <!--  <Date> value is unique 2023-10-06-07:00 -->
            <UniqueID>12354</UniqueID> 
            <Date>2023-10-06-07:00</Date>
            <ExpenseAmount>998.00</ExpenseAmount>
            <BonusAmount>400.00</BonusAmount>
        </Worker>
        <Worker>  <!--  <Date> value is unique 2023-10-06-07:00 -->
            <UniqueID>4567</UniqueID>
            <Date>2023-10-06-07:00</Date>
            <ExpenseAmount>1507.00</ExpenseAmount>
            <BonusAmount>322.00</BonusAmount>
        </Worker>        
    </Records>
    <Records>
        <Worker> <!-- This node needs to repeat as it does'nt have <Date> element -->
            <UniqueID>67896</UniqueID> 
            <ExpenseAmount>400.00</ExpenseAmount>
            <BonusAmount>230.00</BonusAmount>
        </Worker>
        <Worker> <!-- This node needs to repeat as it does'nt have <Date> element -->
            <UniqueID>567899</UniqueID>
            <ExpenseAmount>190.00</ExpenseAmount>
            <BonusAmount>2000.00</BonusAmount>
        </Worker>
        <Worker> <!--  <Date> value is unique 2023-10-06-07:00 -->
            <UniqueID>12354</UniqueID>
            <Date>2023-10-10-07:00</Date>
            <ExpenseAmount>998.00</ExpenseAmount>
            <BonusAmount>400.00</BonusAmount>
        </Worker>   
        <Worker> <!--  <Date> value is unique 2023-10-06-07:00 -->
            <UniqueID>4567</UniqueID>
            <Date>2023-10-10-07:00</Date>
            <ExpenseAmount>1507.00</ExpenseAmount>
            <BonusAmount>322.00</BonusAmount>
        </Worker>        
    </Records>
</Workers>

First <Records> node - <Worker> nodes with <UniqueID>values 67896 and 567899 are placed along with another <Worker> node that has <UniqueID> value 4567 and <Date> 2023-10-06-07:00

My current XSLT

    <?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" version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="Workers">
        
            <xsl:copy>
                <Records>
                    <xsl:copy-of select="Worker[not(exists(Date))]"/>
                    <xsl:for-each-group select="Worker[exists(Date)]" group-by="Date">                       
                        <xsl:copy>
                            <xsl:copy-of select="current-group()[exists(Date)]"/>
                        </xsl:copy>
                    </xsl:for-each-group>
                </Records>
            </xsl:copy>        
    </xsl:template>
</xsl:stylesheet>

My current output

    <?xml version="1.0" encoding="UTF-8"?>
<Workers>
   <Records>
      <Worker>
         <UniqueID>67896</UniqueID>
         <ExpenseAmount>400.00</ExpenseAmount>
         <BonusAmount>230.00</BonusAmount>
      </Worker>
      <Worker>
         <UniqueID>567899</UniqueID>
         <ExpenseAmount>190.00</ExpenseAmount>
         <BonusAmount>2000.00</BonusAmount>
      </Worker>
      <Worker>
         <Worker>
            <UniqueID>12354</UniqueID>
            <Date>2023-10-06-07:00</Date>
            <ExpenseAmount>998.00</ExpenseAmount>
            <BonusAmount>400.00</BonusAmount>
         </Worker>
         <Worker>
            <UniqueID>4567</UniqueID>
            <Date>2023-10-06-07:00</Date>
            <ExpenseAmount>1507.00</ExpenseAmount>
            <BonusAmount>322.00</BonusAmount>
         </Worker>
      </Worker>
      <Worker>
         <Worker>
            <UniqueID>12354</UniqueID>
            <Date>2023-10-10-07:00</Date>
            <ExpenseAmount>998.00</ExpenseAmount>
            <BonusAmount>400.00</BonusAmount>
         </Worker>
         <Worker>
            <UniqueID>4567</UniqueID>
            <Date>2023-10-10-07:00</Date>
            <ExpenseAmount>1507.00</ExpenseAmount>
            <BonusAmount>322.00</BonusAmount>
         </Worker>
      </Worker>
   </Records>
</Workers>

Issues

Nodes with unique <Date> is not being copied along with <Worker> as in the expected output.

Could anyone take a look at and help me understand what I should be doing to get desired output.

Thanks


Solution

  • I am still not sure I follow your logic correctly, though I believe this will return the expected result:

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="/Workers">
        <Workers>
            <xsl:variable name="undated" select="Worker[not(Date)]" />
            <xsl:for-each-group select="Worker" group-by="Date">
                <Records>
                    <xsl:copy-of select="$undated"/>
                    <xsl:copy-of select="current-group()"/>
                </Records>
            </xsl:for-each-group>
        </Workers>
    </xsl:template>
    
    </xsl:stylesheet>