Home > Back-end >  XSLT - Merge same ID elements and get non blank values from same group
XSLT - Merge same ID elements and get non blank values from same group

Time:02-04

Using XSLT 1.0 I need to transform below XML by merging same ID elements and get the non blank values from same ID group and any non present element in one of those same ID group. I did a transformation but only works when all the elements and non blank values are present in the first element of the same ID group.

In below example XML, Amount element is blank in the first payment element, but available in the second payment element of the same ID group. Estimate element is in the other way around, and finally Option element is only present in the first payment element of that same ID group.

Source XML:

<?xml version="1.0" encoding="utf-8"?>
<Data>
    <payment Name="John Curtis">
        <Account Type="Personal" Id="111" Token="aaa"/>
        <Amount/>
        <Estimate Val="30"/>
        <Option Val="Yes"/>
    </payment>
    <payment Name="John Curtis">
        <Account Type="Personal" Id="111" Token="aaa"/>             
        <Amount Paid="10">
            <Breakout>
                <Misc Desc="Interest" Amount="8"/>
                <Misc Desc="Principal" Amount="2"/>
            </Breakout>
        </Amount>
        <Estimate/>
    </payment>
    <payment Name="Elvis Kans">
        <Account Type="Personal" Id="222" Token="bbb"/>
        <Amount Paid="5">
            <Breakout>
                <Misc Desc="Interest" Amount="3"/>
                <Misc Desc="Principal" Amount="2"/>
            </Breakout>
        </Amount>
        <Estimate Val="10"/>
        <Option Val="Yes"/>
    </payment>
</Data> 

Expected output:

<?xml version="1.0" encoding="UTF-8"?>
<output>
   <result>
      <item VAL="Name">John Curtis</item>
      <group>
         <header VAL="Total">10</header>
         <item VAL="Item_1">8</item>
         <item VAL="Item_2">2</item>
      </group>
      <item VAL="Estimate">30</item>
      <item VAL="Option">Yes</item>
   </result>
   <result>
      <item VAL="Name">Elvis Kans</item>
      <group>
         <header VAL="Total">5</header>
         <item VAL="Item_1">3</item>
         <item VAL="Item_2">2</item>
      </group>
      <item VAL="Estimate">10</item>
      <item VAL="Option">Yes</item>
   </result>
</output>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:key name="paymentKey" match="payment" use="concat(Account/@Id, Account/@Token)"/>
    
    <xsl:template match="Data">
        <output>
            <xsl:apply-templates select="@*|payment[generate-id()=generate-id(key('paymentKey', concat(Account/@Id, Account/@Token))[1])]"/>
        </output>
    </xsl:template>
    
    <xsl:template match="payment">
        <xsl:variable name="curr-group" select="key('paymentKey', concat(Account/@Id, Account/@Token))"/>
        <result>
            <xsl:choose>
                <xsl:when test="count($curr-group)=1">
                    <!-- Different Groups -->
                    <xsl:element name="item">
                        <xsl:attribute name="VAL">Name</xsl:attribute>
                        <xsl:value-of select="@Name"/>
                    </xsl:element>
                    <group>
                        <xsl:element name="header">
                            <xsl:attribute name="VAL">Total</xsl:attribute>
                            <xsl:value-of select="Amount/@Paid"/>
                        </xsl:element>
                        <xsl:for-each select="Amount/Breakout/Misc">
                            <xsl:element name="item">
                                <xsl:attribute name="VAL">Item_<xsl:value-of select="position()"/>
                                </xsl:attribute>
                                <xsl:value-of select="@Amount"/>
                            </xsl:element>
                        </xsl:for-each>
                    </group>
                    <xsl:element name="item">
                        <xsl:attribute name="VAL">Estimate</xsl:attribute>
                        <xsl:value-of select="Estimate/@Val"/>
                    </xsl:element>
                    <xsl:element name="item">
                        <xsl:attribute name="VAL">Option</xsl:attribute>
                        <xsl:value-of select="Option/@Val"/>
                    </xsl:element>
                </xsl:when>
                <xsl:otherwise>
                    <!-- Same Groups -->
                    <xsl:element name="item">
                        <xsl:attribute name="VAL">Name</xsl:attribute>
                        <xsl:value-of select="@Name"/>
                    </xsl:element>
                    <group>
                        <xsl:element name="header">
                            <xsl:attribute name="VAL">Total</xsl:attribute>
                            <xsl:value-of select="Amount/@Paid"/>
                        </xsl:element>
                        <xsl:for-each select="Amount/Breakout/Misc">
                            <xsl:element name="item">
                                <xsl:attribute name="VAL">Item_<xsl:value-of select="position()"/>
                                </xsl:attribute>
                                <xsl:value-of select="@Amount"/>
                            </xsl:element>
                        </xsl:for-each>
                    </group>
                    <xsl:element name="item">
                        <xsl:attribute name="VAL">Estimate</xsl:attribute>
                        <xsl:value-of select="Estimate/@Val"/>
                    </xsl:element>
                    <xsl:element name="item">
                        <xsl:attribute name="VAL">Option</xsl:attribute>
                        <xsl:value-of select="Option/@Val"/>
                    </xsl:element>
                </xsl:otherwise>
            </xsl:choose>
        </result>
    </xsl:template>
</xsl:stylesheet>

Can you please guide me on the changes I need to make to accomplish that?

CodePudding user response:

I think in your template instead of the xsl:choose you just want/need

<xsl:template match="payment">
    <xsl:variable name="curr-group" select="key('paymentKey', concat(Account/@Id, Account/@Token))"/>
    <result>
        
                <xsl:element name="item">
                    <xsl:attribute name="VAL">Name</xsl:attribute>
                    <xsl:value-of select="@Name"/>
                </xsl:element>
                <group>
                    <xsl:element name="header">
                        <xsl:attribute name="VAL">Total</xsl:attribute>
                        <xsl:value-of select="sum($curr-group/Amount/@Paid)"/>
                    </xsl:element>
                    <xsl:for-each select="$curr-group/Amount/Breakout/Misc">
                        <xsl:element name="item">
                            <xsl:attribute name="VAL">Item_<xsl:value-of select="position()"/>
                            </xsl:attribute>
                            <xsl:value-of select="@Amount"/>
                        </xsl:element>
                    </xsl:for-each>
                </group>
                <xsl:element name="item">
                    <xsl:attribute name="VAL">Estimate</xsl:attribute>
                    <xsl:value-of select="Estimate/@Val"/>
                </xsl:element>
                <xsl:element name="item">
                    <xsl:attribute name="VAL">Option</xsl:attribute>
                    <xsl:value-of select="Option/@Val"/>
                </xsl:element>

    </result>
</xsl:template>

With that change I get the result

<output>
  <result>
    <item VAL="Name">John Curtis</item>
    <group>
      <header VAL="Total">10</header>
      <item VAL="Item_1">8</item>
      <item VAL="Item_2">2</item>
    </group>
    <item VAL="Estimate">30</item>
    <item VAL="Option">Yes</item>
  </result>
  <result>
    <item VAL="Name">Elvis Kans</item>
    <group>
      <header VAL="Total">5</header>
      <item VAL="Item_1">3</item>
      <item VAL="Item_2">2</item>
    </group>
    <item VAL="Estimate">10</item>
    <item VAL="Option">Yes</item>
  </result>
</output>
  • Related