this is an elegant template, really shows the power and sweetspot hit by
XSLT 2.0 (anyone doing this in XSLT 1.0, barring FXSL, this becomes a
bit of a nightmare)....any alternate approaches which makes things more
efficient (i dont think its possible to optimise the MKay recursive
approach).
Why bar FXSL -- are we masochistic? :oO)
Here's my take on this problem. Trying to adhere to the KISS
principle, I'm using a two-step approach naturally expressed as
functional composition.
Notice that not only is this a litlle easier to understand, but that
the main "powerhorse" (the function f:makeTree() ) is just 17 lines of
code:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="f xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:sequence select="f:makeTree(f:tokenizePaths(/*/*), 1)"/>
</xsl:template>
<xsl:function name="f:tokenizePaths">
<xsl:param name="pfilePaths" as="element()+"/>
<xsl:for-each select="$pfilePaths">
<item>
<xsl:for-each select="tokenize(.,'/')">
<q><xsl:value-of select="."/></q>
</xsl:for-each>
</item>
</xsl:for-each>
</xsl:function>
<xsl:function name="f:makeTree" as="element()*">
<xsl:param name="pItems" as="element()*"/>
<xsl:param name="pqLevel" as="xs:integer"/>
<xsl:for-each-group select="$pItems" group-by="q[$pqLevel]">
<xsl:choose>
<xsl:when test="count(current-group()) = 1">
<file name="{q[$pqLevel]}"/>
</xsl:when>
<xsl:otherwise>
<dir name="{q[$pqLevel]}">
<xsl:sequence select="f:makeTree(current-group(), $pqLevel+1)"/>
</dir>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>
--
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play
On 1/11/07, James Fuller <jim(_dot_)fuller(_at_)ruminate(_dot_)co(_dot_)uk>
wrote:
Andrew Welch wrote:
> On 1/11/07, James Fuller <jim(_dot_)fuller(_at_)ruminate(_dot_)co(_dot_)uk>
wrote:
>
>> Hello All,
>>
>> Can anyone propose a pure xslt (1 or 2) solution to transforming the
>> following flat xml structure of directory paths into a hierarchical
>> (nested) xml.
>>
>> <?xml version='1.0'?>
>> <listing>
>> <item>cn/test.xml</item>
>> <item>en</item>
>> <item>en/test.html</item>
>> <item>en/test1.html</item>
>> <item>en/resource</item>
>> <item>en/resource/style</item>
>> <item>en/resource/style/test.css</item>
>> <item>favicon.ico</item>
>> <item>cn</item>
>> </listing>
>>
>> to
>>
>>
>> <dir>
>> <file name="favicon.ico"/>
>> <dir name="cn">
>> <file name="test.xml"/>
>> </dir>
>> <dir name="en">
>> <file name="test.html"/>
>> <file name="test1.html"/>
>> <dir name="resource">
>> <dir name="style">
>> <file name="test.css"/>
>> </dir>
>> </dir>
>> </dir>
>> </dir>
>
>
>
> ...trickier than it first looks :)
>
> This should do the job:
>
> <xsl:stylesheet version="2.0"
> xmlns:xs="http://www.w3.org/2001/XMLSchema"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> exclude-result-prefixes="xs">
>
> <xsl:template match="listing">
> <dir>
> <xsl:call-template name="process">
> <xsl:with-param name="depth" select="1" as="xs:integer"/>
> <xsl:with-param name="seq" select="item"/>
> </xsl:call-template>
> </dir>
> </xsl:template>
>
> <xsl:template name="process">
> <xsl:param name="depth" as="xs:integer"/>
> <xsl:param name="seq"/>
> <xsl:for-each-group select="$seq" group-by="tokenize(.,
> '/')[$depth]">
> <xsl:variable name="part" select="tokenize(., '/')[$depth]"/>
> <xsl:choose>
> <xsl:when test="contains($part, '.')">
> <file name="{$part}"/>
> </xsl:when>
> <xsl:otherwise>
> <dir name="{$part}">
> <xsl:call-template name="process">
> <xsl:with-param name="depth" select="$depth +
> 1"/>
> <xsl:with-param name="seq"
> select="$seq[tokenize(., '/')[$depth]
> = $part]"/>
> </xsl:call-template>
> </dir>
> </xsl:otherwise>
> </xsl:choose>
> </xsl:for-each-group>
> </xsl:template>
>
> </xsl:stylesheet>
>
>
> The output is:
>
> <dir>
> <dir name="cn">
> <file name="test.xml"/>
> </dir>
> <dir name="en">
> <file name="test.html"/>
> <file name="test1.html"/>
> <dir name="resource">
> <dir name="style">
> <file name="test.css"/>
> </dir>
> </dir>
> </dir>
> <file name="favicon.ico"/>
> </dir>
nice....the recursion route seems to be the general approach, just about
scratched it out myself (w/o the part bit)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="data">
<dir name="root">
<xsl:call-template name="pdir">
<xsl:with-param name="n" select="1"/>
<xsl:with-param name="in" select="//item"/>
</xsl:call-template>
</dir>
</xsl:template>
<xsl:template name="pdir">
<xsl:param name="n"/>
<xsl:param name="in"/>
<xsl:for-each-group select="$in" group-by="tokenize(.,'/')[$n]">
<dir src="{.}">
<xsl:call-template name="pdir">
<xsl:with-param name="n" select="$n+1"/>
<xsl:with-param name="in" select="current-group()"/>
</xsl:call-template> </dir>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
this is an elegant template, really shows the power and sweetspot hit by
XSLT 2.0 (anyone doing this in XSLT 1.0, barring FXSL, this becomes a
bit of a nightmare)....any alternate approaches which makes things more
efficient (i dont think its possible to optimise the MKay recursive
approach).
was thinking there might be an approach using analyze-string
instruction...but still working on it.
(IMHO, DaveP should include this in XSLT FAQ, its a keeper and common
processing scenario)
ta, J
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--