Re: [xsl] Building a tree from path-like strings

Subject: Re: [xsl] Building a tree from path-like strings
From: "Dimitre Novatchev" <dnovatchev@xxxxxxxxx>
Date: Sat, 3 Jan 2009 15:05:03 -0800
Happy New Year and Best Wishes to Everyone !

On Sat, Jan 3, 2009 at 10:38 AM, Richard Lewis <richard.lewis@xxxxxxxxxx> wrote:
> Hi there,
>
> I'm trying to make a tree-hierarchic document from a flat
> list of elements which have id-like values which define
> the hierarchy I need.

In addition to G. Ken Holman's XSLT 1.0 solution, here is an XSLT 2.0 one.

The transformation below produces an XML document from the id-strings:

<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
	xmlns:xs="http://www.w3.org/2001/XMLSchema";
	xmlns:my="my:fn"
	exclude-result-prefixes="my xs"
	>
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  	
	<xsl:template match="/*/*">
	  <FOO>
	    <xsl:sequence select=
	     "my:makeTree(*/field[@name='external_id']/value, 'FOO/')"/>
	  </FOO>
	</xsl:template>

	<xsl:function name="my:makeTree">
	  <xsl:param name="pIdNodes" as="element()*"/>
	  <xsl:param name="pPrefix" as="xs:string"/>
	
	  <xsl:for-each-group
	      select="$pIdNodes[starts-with(.,$pPrefix)]" group-by=
	     "substring-before(concat(substring-after(.,$pPrefix),
	                                                '/'),
                                                       '/')">
		    <xsl:variable name="vElName" select=
		     "concat('V-',current-grouping-key())"/>

		    <xsl:variable name="vNewPrefix" select=
		     "concat($pPrefix, current-grouping-key(),'/')"/>
		
		    <xsl:element name="{$vElName}">
		      <xsl:sequence select=
		       "my:makeTree($pIdNodes, $vNewPrefix)"/>
		    </xsl:element>
                         </xsl:for-each-group>
	</xsl:function>
</xsl:stylesheet>


When the above is applied on the originally provided XML document:

<btc>
	<record table="works">
		<record table="external_records">
			<field name="external_id">
				<value>FOO/1</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/1/1</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/1/2</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/2</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/2/1</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/2/1/1</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/2/1/2</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/2/2</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
		<record table="external_records">
			<field name="external_id">
				<value>FOO/3</value>
			</field>
			<field name="ds_name">
				<value>ms-sources</value>
			</field>
		</record>
	</record>
</btc>

the result is:

<FOO>
   <V-1>
      <V-1/>
      <V-2/>
   </V-1>
   <V-2>
      <V-1>
         <V-1/>
         <V-2/>
      </V-1>
      <V-2/>
   </V-2>
   <V-3/>
</FOO>

Current Thread