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/