[xsl] Grouping hierarchy path elements

Subject: [xsl] Grouping hierarchy path elements
From: "Daniel Geske" <Daniel.Geske@xxxxxx>
Date: Mon, 23 Aug 2004 14:14:04 +0200
Hi All,

I've been working on an XSLT for days now, and cannot find the solution to
one problem. I have a source XML document containing one level of elements.
Each element contains a path element that holds information on what path
the element was extracted from.

Now, through XSL transformation, I would like to recreate the tree
structure of the items.
To make things clearer, I created an example.

The source XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<items>
  <item id="1">
     <path>root\folderA\folderB\folderC</path>
  </item>
  <item id="2">
     <path>root\folderA\folderB\folderD</path>
  </item>
  <item id="3">
     <path>root\folderA\folderE\folderF</path>
  </item>
  <item id="4">
     <path>root\folderG</path>
  </item>
  <item id="5">
     <path>root\folderA\folderB\folderD</path>
  </item>
</items>

This is what I would like to get when done transforming:
<?xml version="1.0" encoding="UTF-8"?>
<transformedDocument>
  <path name="root">
     <path name="folderA">
       <path name="folderB">
          <path name="folderC">
            <item id="1"/>
          </path>
          <path name="folderD">
            <item id="2"/>
            <item id="5"/>
          </path>
       </path>
       <path name="folderE">
          <path name="folderF">
            <item id="3"/>
          </path>
       </path>
     </path>
     <path name="folderG">
       <item id="4"/>
     </path>
  </path>
</transformedDocument>

So far, my transformation groups the paths using the Muenchian method,
sorts by path and then calls a template that calls itself recursively,
splitting at each '\' character and inserts all items with a matching path:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform";>
  <xsl:key name="items" match="item" use="path"/>
  <xsl:variable name="pathSeparator">
     <xsl:text>\</xsl:text>
  </xsl:variable>
  <xsl:template match="/items">
     <transformedDocument>
       <xsl:for-each select="//item[generate-id(.)=generate-id(key('items',
path))]">
          <xsl:sort select="path"/>
          <xsl:call-template name="item">
            <xsl:with-param name="path" select="path"/>
            <xsl:with-param name="fullPath" select="path"/>
          </xsl:call-template>
       </xsl:for-each>
     </transformedDocument>
  </xsl:template>
  <xsl:template name="item">
     <xsl:param name="path" select="$path"/>
     <xsl:param name="fullPath" select="$fullPath"/>
     <xsl:variable name="name">
       <xsl:value-of select="substring-before($path, $pathSeparator)"/>
     </xsl:variable>
     <xsl:choose>
       <xsl:when test="contains($path, $pathSeparator)">
          <path>
            <xsl:attribute name="name"><xsl:value-of select="$name"/></
xsl:attribute>
            <xsl:call-template name="item">
               <xsl:with-param name="path" select="substring-after($path,
$pathSeparator)"/>
               <xsl:with-param name="fullPath" select="$fullPath"/>
            </xsl:call-template>
          </path>
       </xsl:when>
       <xsl:otherwise>
          <path>
            <xsl:attribute name="name"><xsl:value-of select="$path"/></
xsl:attribute>
            <xsl:comment>
               <xsl:value-of select="$fullPath"/>
            </xsl:comment>
            <xsl:for-each select="//item">
               <xsl:if test="path[.=$fullPath]">
                 <item>
                    <xsl:attribute name="id"><xsl:value-of select="
attribute::id"/></xsl:attribute>
                 </item>
               </xsl:if>
            </xsl:for-each>
          </path>
       </xsl:otherwise>
     </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

The problem - I canot figure out how to group parent path names in the
destination document. I cannot come up with a solution and would be glad to
be able to avoid this:
<?xml version="1.0" encoding="UTF-8"?>
<transformedDocument>
  <path name="root">
     <path name="folderA">
       <path name="folderB">
          <path name="folderC">
            <!--root\folderA\folderB\folderC-->
            <item id="1" />
          </path>
       </path>
     </path>
  </path>
  <path name="root">
     <path name="folderA">
       <path name="folderB">
          <path name="folderD">
            <!--root\folderA\folderB\folderD-->
            <item id="2" />
            <item id="5" />
          </path>
       </path>
     </path>
  </path>
  <path name="root">
     <path name="folderA">
       <path name="folderE">
          <path name="folderF">
            <!--root\folderA\folderE\folderF-->
            <item id="3" />
          </path>
       </path>
     </path>
  </path>
  <path name="root">
     <path name="folderG">
       <!--root\folderG-->
       <item id="4" />
     </path>
  </path>
</transformedDocument>

I hope to hear from you all soon. Thank you very much.

Mit freundlichen Gr|_en / Sincerely

Daniel Geske


Telematik/Infotainment, AE-V32
Telematics/Infotainment, AE-V32

IAV GmbH
Ingenieurgesellschaft Auto und Verkehr
Carnotstra_e 1
10587 Berlin
Germany

Tel.:  +49  (30)  3 99 78 - 90 44
Fax: +49  (30)  3 99 78 - 94 11

E-mail: <mailto:daniel.geske@xxxxxx>
Internet: http://www.iav.de

Current Thread