This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: Transforming non structured XML into structured XML with XSLT
- To: bruno dot o dot faure at bnpparibas dot com
- Subject: [xsl] Re: Transforming non structured XML into structured XML with XSLT
- From: Dimitre Novatchev <dnovatchev at yahoo dot com>
- Date: Wed, 8 Aug 2001 08:13:26 -0700 (PDT)
- Cc: xsl-list at lists dot mulberrytech dot com
- Reply-To: xsl-list at lists dot mulberrytech dot com
bruno.o.faure@bnpparibas.com wrote:
> I'm trying to write a XSLT style sheet that transforms a non-structured xml
> document to a structured xml document and am in troubles :
>
> My xml documents looks like a flat modelization of a treeview :
>
> <row lib="data1" folder1="A" folder2="C" />
> <row lib="data2" folder1="A" folder2="C" />
> <row lib="data3" folder1="A" folder2="C" />
> <row lib="data1" folder1="A" folder2="D" />
> <row lib="data2" folder1="A" folder2="D" />
> <row lib="data3" folder1="A" folder2="D" />
> <row lib="data1" folder1="B" folder2="E" />
> <row lib="data2" folder1="B" folder2="E" />
> <row lib="data3" folder1="B" folder2="E" />
> <row lib="data1" folder1="B" folder2="F" />
> <row lib="data2" folder1="B" folder2="F" />
> <row lib="data3" folder1="B" folder2="F" />
> <row lib="data1" folder1="B" folder2="G" />
> <row lib="data2" folder1="B" folder2="G" />
> <row lib="data3" folder1="B" folder2="G" />
> <row lib="data1" folder1="B" folder2="H" />
> <row lib="data2" folder1="B" folder2="H" />
> <row lib="data3" folder1="B" folder2="H" />
>
> Basically the first line means that the folder A contains the folder C which
> contains data1
> And so on for the others
> I would need to transform it in the following structured XML document :
>
> <A>
> <C>
> <data1/>
> <data2/>
> <data3/>
> </C>
> <D>
> <data1/>
> <data2/>
> <data3/>
> </D>
> </A>
> <B>
> <E>
> <data1/>
> <data2/>
> <data3/>
> </E>
> <F>
> <data1/>
> <data2/>
> <data3/>
> </F>
> [...]
> </B>
>
> The XML file is dynamically generated so folder names and folder depth are not
> know in advance.
> I can't see how to do that, and I don't event know if it is possible.
Hi Bruno,
Here's the solution:
xml source:
----------
<rows>
<row lib="data1" folder1="A" folder2="C" />
<row lib="data2" folder1="A" folder2="C" />
<row lib="data3" folder1="A" folder2="C" />
<row lib="data1" folder1="A" folder2="D" />
<row lib="data2" folder1="A" folder2="D" />
<row lib="data3" folder1="A" folder2="D" />
<row lib="data1" folder1="B" folder2="E" />
<row lib="data2" folder1="B" folder2="E" />
<row lib="data3" folder1="B" folder2="E" />
<row lib="data1" folder1="B" folder2="F" />
<row lib="data2" folder1="B" folder2="F" />
<row lib="data3" folder1="B" folder2="F" />
<row lib="data1" folder1="B" folder2="G" />
<row lib="data2" folder1="B" folder2="G" />
<row lib="data3" folder1="B" folder2="G" />
<row lib="data1" folder1="B" folder2="H" />
<row lib="data2" folder1="B" folder2="H" />
<row lib="data3" folder1="B" folder2="H" />
</rows>
xsl stylesheet:
--------------
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kRow_f1_f2" match="row" use="concat(@folder1, '~', @folder2)"/>
<xsl:key name="kRow_f1" match="row" use="@folder1"/>
<xsl:key name="kRow_f2" match="row" use="@folder2"/>
<xsl:variable name="manySpaces" select="' '"/>
<xsl:variable name="linkList" select="/rows/row" />
<xsl:template match="/">
<xsl:call-template name="constructHierarchy">
<xsl:with-param name="linkList" select="$linkList" />
</xsl:call-template>
</xsl:template>
<xsl:template name="constructHierarchy">
<xsl:param name="linkList" select="/.." />
<xsl:for-each select="$linkList[not(key('kRow_f2', @folder1))]">
<xsl:if test="count(. | key('kRow_f1', @folder1)[1])=1">
<xsl:call-template name="makeElement">
<xsl:with-param name="pElement" select="@folder1"/>
<xsl:with-param name="indent" select="0"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="makeElement">
<xsl:param name="pElement" select="'ErrorUnknown'"/>
<xsl:param name="indent" select="0"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="substring($manySpaces, 1, $indent)"/>
<xsl:element name="{$pElement}">
<xsl:for-each select="key('kRow_f1', $pElement)
[
count(. | key('kRow_f1_f2',
concat(@folder1,
'~',
@folder2
)
)[1]
) = 1
]">
<xsl:call-template name="makeElement">
<xsl:with-param name="pElement" select="@folder2"/>
<xsl:with-param name="indent" select="$indent + 2"/>
</xsl:call-template>
</xsl:for-each>
<xsl:for-each select="key('kRow_f2', $pElement)">
<xsl:element name="{@lib}"/>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result:
------
<A>
<C>
<data1 />
<data2 />
<data3 />
</C>
<D>
<data1 />
<data2 />
<data3 />
</D>
</A>
<B>
<E>
<data1 />
<data2 />
<data3 />
</E>
<F>
<data1 />
<data2 />
<data3 />
</F>
<G>
<data1 />
<data2 />
<data3 />
</G>
<H>
<data1 />
<data2 />
<data3 />
</H>
</B>
As you can see, a minor thing remainds -- the indentation (if needed at all) could
be improved.
Hope this helped.
Cheers,
Dimitre Novatchev.
__________________________________________________
Do You Yahoo!?
Make international calls for as low as $.04/minute with Yahoo! Messenger
http://phonecard.yahoo.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list