xsl-list
[Top] [All Lists]

Re: [xsl] most efficient flat file listing to hierarchical

2007-01-11 03:26:16
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>

cheers
andrew

--~------------------------------------------------------------------
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>
--~--