Skip to Content

XSLT is a great tool in transforming the xml data in complex scenarios.I recently faced a problem related to sorting of a sub tree based on a grand child field keeping the other nodes in the tree as they are. The requirement was related to a segment in an IDoc. I would explain the problem with an imaginary example mentioned here:

We have a root node here. Then Employee data which contains Employee Name, Employee Address and Card data segments.

initial view.JPG

EmployeName and EmployeeAddress tags have nothing do with sorting. They have general information. We will have a closer look on CardData.

CardData.JPG

Requirement is that we have to sort CardData based on

CardData-CardDetails-CardNumber.

i.e. we need to have this xml sorted on CardData level but on a grand child (CardNumber).

So all other data should remain as it is but CardData should be sorted on a CardNumber (grandchild to CardData.). Simple XSLT sort would not work here as we do not want to sort parent on child.

Solution

We will be using an XSLT template to copy the nodes as they are for all the nodes which are not CardData.

template_to_copy_node_as_it_is.JPG

Here is the main logic to copy other nodes as they are and sort CardData based on CardNumber

main_logic.JPG

Line 15 – Matching Employee Data

Line 18 – Select all the nodes which are not CardData. The template for copying the node as it is will copy all the nodes as they are (with this select).

Line 20 – Select all the nodes CardData[CardDetails]

Line 21 – Sort the data selected in line 20 based on CardDetails/CardNumber.

And that’s it. Here is the output

Output.JPG

Here is the complete XSLT program, source file and output

XSLT Code

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
<!-- Template to copy the nodes as they are -->
  <xsl:template match="@* | node()">
  <xsl:copy>
   <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
 </xsl:template>
<!-- Matching the template for Employee Data -->
 <xsl:template match="EmployeData">
    <xsl:copy>
        <!--If the node is not CardData it should be copied as it is -->
         <xsl:apply-templates select="*[not(self::CardData)]"></xsl:apply-templates>
         <!-- if the node is CardData, It should be sorted based on CardDetails/CardNumber field-->
             <xsl:apply-templates select="*[self::CardData[CardDetails]]">   
                     <xsl:sort select="./CardDetails/CardNumber" data-type="number" order="ascending"/>            
            </xsl:apply-templates>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Source XML

<Root>
    <EmployeData>
        <EmployeeName>
            <FirstName>Angela</FirstName>
            <LastName>Smith</LastName>
        </EmployeeName>
        <EmployeeAddress>
            <HouseNo>150</HouseNo>
            <StreetName>Southampton Street</StreetName>
            <City>Richmond</City>
            <State>St</State>
            <Pin>12345</Pin>
        </EmployeeAddress>
        <CardData>
            <CardDetails>
                <CardNumber>2</CardNumber>
                <DateofExpiry>20 June 2015</DateofExpiry>
            </CardDetails>           
        </CardData>
        <CardData>       
            <CardDetails>
                <CardNumber>4</CardNumber>
                <DateofExpiry>20 June 2015</DateofExpiry>
            </CardDetails>
            </CardData>   
        <CardData>           
            <CardDetails>
                <CardNumber>1</CardNumber>
                <DateofExpiry>20 June 2015</DateofExpiry>
            </CardDetails>           
        </CardData>       
    </EmployeData>
</Root>

Output

<Root>
    <EmployeData>
        <EmployeeName>
            <FirstName>Angela</FirstName>
            <LastName>Smith</LastName>
        </EmployeeName>
        <EmployeeAddress>
            <HouseNo>150</HouseNo>
            <StreetName>Southampton Street</StreetName>
            <City>Richmond</City>
            <State>St</State>
            <Pin>12345</Pin>
        </EmployeeAddress>
        <CardData>
            <CardDetails>
                <CardNumber>1</CardNumber>
                <DateofExpiry>20 June 2015</DateofExpiry>
            </CardDetails>
        </CardData>
        <CardData>
            <CardDetails>
                <CardNumber>2</CardNumber>
                <DateofExpiry>20 June 2015</DateofExpiry>
            </CardDetails>
        </CardData>
        <CardData>
            <CardDetails>
                <CardNumber>4</CardNumber>
                <DateofExpiry>20 June 2015</DateofExpiry>
            </CardDetails>
        </CardData>
    </EmployeData>
</Root>

Important Points

1) The generic template to copy the node as it is, can be reused in other codes. This is a common XSLT code used to copy the nodes by default.

2) The main logic resides in selecting the sub tree and sorting on grand child field. This can be achieved through different XSLT code as well but I find this way simple and short.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply