xsl-list
[Top] [All Lists]

Re: Grouping hierarchy path elements

2004-08-23 06:04:45
Hi Daniel,

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.

Here's a set of templates that works with your example.

The main template is the transformDocument template. This takes a path
(an initial part of a path) and a set of items (whose paths should all
start with the $path). It works out the next step in the path for the
first item and from that creates a new path. Then it sorts the items
into three groups:

  - items whose path *is* the new path, which should just be output
  - items whose path *starts with* the new path, which need to be
    processed again by this template, with the new path
  - items whose path *doesn't* start with the new path, which need to
    be processed again by this template, with the current path

The result of the first two of these groups gets put within a <path>
element, and the result of the third of these groups gets inserted
afterwards.

<xsl:template name="transformDocument">
  <xsl:param name="path"/>
  <xsl:param name="items" select="/.."/>
  <xsl:if test="$items">
    <xsl:variable name="step">
      <xsl:variable name="rest" select="substring-after($items[1]/path, 
concat($path, '\'))" />
      <xsl:choose>
        <xsl:when test="contains($rest, '\')">
          <xsl:value-of select="substring-before($rest, '\')" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$rest" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="newPath" select="concat($path, '\', $step)" />
    <path name="{$step}">
      <xsl:apply-templates select="$items[path = $newPath]"/>
      <xsl:call-template name="transformDocument">
        <xsl:with-param name="path" select="$newPath" />
        <xsl:with-param name="items" select="$items[starts-with(path, $newPath) 
and
                                                    path != $newPath]"/>
      </xsl:call-template>
    </path>
    <xsl:call-template name="transformDocument">
      <xsl:with-param name="path" select="$path"/>
      <xsl:with-param name="items" select="$items[not(starts-with(path, 
$newPath))]"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>  

The next template matches the <items> element and starts off the
processing. I've assumed that there's only one root here.

<xsl:template match="items">
  <transformedDocument>
    <xsl:variable name="root" select="substring-before(item[1]/path, '\')" />
    <path name="{$root}">
      <xsl:call-template name="transformDocument">
        <xsl:with-param name="path" select="$root" />
        <xsl:with-param name="items" select="item" />
      </xsl:call-template>
    </path>
  </transformedDocument>
</xsl:template>
  
The final template just outputs whatever you want for each item; here,
a copy of the <item> element without its child <path> element.

<xsl:template match="item">
  <item id="{(_at_)id}" />
</xsl:template>

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/



<Prev in Thread] Current Thread [Next in Thread>