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>