XSL for Converting Flat XML to Hierarchial XML

Just for the reference of XML, XSL enthusiasts, I will quickly demonstrate how you can convert a flat XML file to a hierarchial XML with nested nodes. When hierarchy is the focus, there can be many different XML structures, but for the sake of simplicity, I have chosen the below example. This example assumes that you have ID, PARENTID and LEVEL attributes in each node, and PARENTID being 0, blank or null means that the employee node is a root node. To achieve this conversion, we have to know the root nodes.

Again to make it a little bit richer example, I am including 2 companies and their organizational structure in a single flat XML.

XML Data

<companylist>
       <company CompanyID="1" CompanyName="ACME Corporation"/>
       <company CompanyID="2" CompanyName="XYZ Ltd."/>
       <employee ID="1" CompanyID="1" Name="ALLY" />
       <employee ID="2" PARENTID="1" CompanyID="1" Name="RON" />
       <employee ID="3" PARENTID="1" CompanyID="1" Name="LUCY" />
       <employee ID="59" PARENTID="" CompanyID="1" Name="JOHN" />
       <employee ID="60" PARENTID="59" CompanyID="1" Name="MARY" />
       <employee ID="61" PARENTID="60" CompanyID="1" Name="BILL" />
       <employee ID="101" CompanyID="2" Name="STEVE" />
       <employee ID="102" PARENTID="101" CompanyID="2" Name="PAUL" />
</companylist>

Notice that while XYZ Ltd has one root, ACME Corporation has two root nodes and therefore 2 different hierarchies. Giving us a total of 3 subtrees.

XSL Code

First, we need to find out the companies and their root level employees, then we can start building each hierarchy.
I think the whole XSL code is pretty straight forward and does not need too much explanation. But in case you have a question or a suggestion, please do not hesitate to comment.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>

	<xsl:template match="/companylist">
		<companylist>
		<xsl:for-each select="//company">
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				<xsl:for-each select="//employee[@CompanyID=current()/@CompanyID and (@PARENTID='0' or @PARENTID='' or not(@PARENTID))]">
					<xsl:copy>
						<xsl:copy-of select="@*"/>
						<xsl:call-template name="getChildren">
							<xsl:with-param name="parid" select="@ID"/>
							<xsl:with-param name="companyid" select="@CompanyID"/>
						</xsl:call-template>
					</xsl:copy>
				</xsl:for-each>
			</xsl:copy>
		</xsl:for-each>
		</companylist>
	</xsl:template>
	<xsl:template name="getChildren">
		<xsl:param name="parid"/>
		<xsl:param name="companyid"/>
		<xsl:for-each select="//employee[@CompanyID=$companyid and @PARENTID=$parid]">
			<xsl:variable name="hierID" select="@ID"/>
			<xsl:copy>
				<xsl:copy-of select="@*"/>
				<xsl:if test="count(//employee[@CompanyID=$companyid and @PARENTID = $hierID]) &gt; 0">
					<xsl:call-template name="getChildren">
						<xsl:with-param name="parid" select="@ID"/>
						<xsl:with-param name="companyid" select="@CompanyID"/>
					</xsl:call-template>
				</xsl:if>
			</xsl:copy>
		</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>

XML Result

<?xml version="1.0" encoding="utf-8"?>
<companylist>
   <company CompanyID="2" CompanyName="XYZ Ltd.">
      <employee CompanyID="2" ID="101" Name="STEVE">
         <employee CompanyID="2" ID="102" Name="PAUL" PARENTID="101"/>
      </employee>
   </company>
   <company CompanyID="1" CompanyName="ACME Corporation">
      <employee CompanyID="1" ID="1" Name="ALLY">
         <employee CompanyID="1" ID="2" Name="RON" PARENTID="1"/>
         <employee CompanyID="1" ID="3" Name="LUCY" PARENTID="1"/>
      </employee>
      <employee CompanyID="1" ID="59" Name="JOHN" PARENTID="">
         <employee CompanyID="1" ID="60" Name="MARY" PARENTID="59">
            <employee CompanyID="1" ID="61" Name="BILL" PARENTID="60"/>
         </employee>
      </employee>
   </company>
</companylist>

..

Advertisements

One Comment on “XSL for Converting Flat XML to Hierarchial XML”

  1. Vladimir Valev says:

    Hi, Cem!
    Excelent post! I had an issues to solve very similar to described on your blog.
    Thank you,
    With regards,
    Vladimir Valev,
    Sofia, Bulgaria


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s