I am trying to learn xslt and I'm having difficulties trying to figure out how to process a file that is not built out as a hierarchy (except based on an attribute) and change it into a hierarchy.

Using the following XML

<?xml version="1.0" encoding="UTF-8"?>
<data>
<h level="1">test</h>
<p>some data for test</p>
<h level="2">test2</h>
<p>some data for test2</p>
<h level="3">test3</h>
<p>some data for test3</p>
<h level="2">test4</h>
<p>some data for test4</p>
<h level="1">test5</h>
<p>some data for test5</p>
<h level="1">test6</h>
<p>some data for test6</p>
<h level="2">test7</h>
<p>some data for test7</p>
</data>

I'd like to convert it to look like:

<data>
<node name="test">
	<text>some data for test</text>
	<node name="test2">
		<text>some data for test2</text>
		<node name="test3">
			<text>some data for test3</text>
		</node>
	</node>
	<node name="test4>
		<text>some data for test4</text>
	</node>
</node>
<node name="test5">
	<text>some data for test5</text>
</node>
<node name="test6">
	<text>some data for test6</text>
	<node name="test7">
		<text>some data for test7</text>
	</node>
</node>
</data>

Thanks for any pointers you can give me that would start me on my way.

As well as the hierarchical method demonstrated on Dave Pawson's site , you can also use recursion. Here is a first cut at a recursion-based solution. It mostly works except it fails to handle the "text4" element correctly. Unfortunately I do not have time at present to figure out how to handle that particular case.

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

  <xsl:template match="/data">
     <xsl:element name="data">
        <xsl:call-template name="node">
           <xsl:with-param name="list" select="//h | //p" />
           <xsl:with-param name="level" select="0" />
        </xsl:call-template>
     </xsl:element>
  </xsl:template>

  <xsl:template name="node">
     <xsl:param name="list" />
     <xsl:param name="level" />

     <xsl:variable name="curlevel" select="$list[1]/@level" />

     <xsl:choose>
     <xsl:when test="$list and $curlevel &gt; $level" >
        <xsl:element name="node">
            <xsl:attribute name="name"><xsl:value-of select="$list[1]"/></xsl:attribute>
            <xsl:element name="text"><xsl:value-of select="$list[2]" /></xsl:element>

            <xsl:call-template name="deepnode">
               <xsl:with-param name="list" select="$list[position() > 2]" />
               <xsl:with-param name="level" select="$curlevel" />
            </xsl:call-template>

        </xsl:element>
        <xsl:call-template name="node">
           <xsl:with-param name="list" select="$list[position() > 2]" />
           <xsl:with-param name="level" select="$level" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
      </xsl:otherwise>
      </xsl:choose>

  </xsl:template>

  <xsl:template name="deepnode">
     <xsl:param name="list" />
     <xsl:param name="level" />

     <xsl:variable name="curlevel" select="$list[1]/@level" />

     <xsl:if test="$list and $curlevel &gt; $level" >
        <xsl:element name="node">
            <xsl:attribute name="name"><xsl:value-of select="$list[1]"/></xsl:attribute>
            <xsl:element name="text"><xsl:value-of select="$list[2]" /></xsl:element>
            <xsl:call-template name="node">
               <xsl:with-param name="list" select="$list[position() > 2]" />
               <xsl:with-param name="level" select="$curlevel" />
            </xsl:call-template>
        </xsl:element>
     </xsl:if>
  </xsl:template>

</xsl:stylesheet>
This article has been dead for over six months. Start a new discussion instead.