Subject: Re: Hierarchy based on indentation (long)
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Sun, 8 Dec 2002 14:49:16 -0800 (PST)
|
"Grainne Reilly" <greilly1@xxxxxxxxx> wrote in message
news:5.1.0.14.0.20021208153308.00b38b08@xxxxxxxxxxxxxxxxx
> Hi,
> Another long question from me.
> I have a source xml similar to that shown below - where there is an
> implicit hierarchy based on the indentation of the content of each
cell.
I
> want to transform it to the result xml also shown below. I have
written a
> stylesheet which seems to achieve this, but being a novice at xslt I
am
> sure that there are much better ways to achieve this result. I would
> really appreciate any suggestions to improve on this.
> Thanks,
> Grainne.
>
> ==========
> source xml
> ==========
> <?xml version="1.0" encoding="UTF-8"?>
> <table>
> <row><cell>Level one description(1)</cell></row>
> <row><cell> Level two description(2)</cell></row>
> <row><cell> Level three description(3)</cell></row>
> <row><cell> Level two description(4)</cell></row>
> <row><cell> Level three description(5)</cell></row>
> <row><cell>Level one description(6)</cell></row>
> <row><cell> Level two description(7)</cell></row>
> <row><cell> Level three description(8)</cell></row>
> <row><cell> Level four description(9)</cell></row>
> <row><cell> Level two description(10)</cell></row>
> </table>
>
> ==========
> result xml
> ==========
> <?xml version="1.0" encoding="UTF-8"?>
> <descriptions>
> <description row="1" level="1">Level one
description(1)</description>
> </descriptions>
> <descriptions>
> <description row="2" level="1">Level one
description(1)</description>
> <description row="2" level="2"> Level two
description(2)</description>
> </descriptions>
> <descriptions>
> <description row="3" level="1">Level one
description(1)</description>
> <description row="3" level="2"> Level two
description(2)</description>
> <description row="3" level="3"> Level three
> description(3)</description>
> </descriptions>
> <descriptions>
> <description row="4" level="1">Level one
description(1)</description>
> <description row="4" level="2"> Level two
description(4)</description>
> </descriptions>
> <descriptions>
> <description row="5" level="1">Level one
description(1)</description>
> <description row="5" level="3"> Level two
description(4)</description>
> <description row="5" level="4"> Level three
> description(5)</description>
> </descriptions>
> <descriptions>
> <description row="6" level="1">Level one
description(6)</description>
> </descriptions>
> <descriptions>
> <description row="7" level="1">Level one
description(6)</description>
> <description row="7" level="2"> Level two
description(7)</description>
> </descriptions>
> <descriptions>
> <description row="8" level="1">Level one
description(6)</description>
> <description row="8" level="2"> Level two
description(7)</description>
> <description row="8" level="3"> Level three
> description(8)</description>
> </descriptions>
> <descriptions>
> <description row="9" level="1">Level one
description(6)</description>
> <description row="9" level="2"> Level two
description(7)</description>
> <description row="9" level="3"> Level three
> description(8)</description>
> <description row="9" level="4"> Level four
> description(9)</description>
> </descriptions>
> <descriptions>
> <description row="10" level="1">Level one
description(6)</description>
> <description row="10" level="2"> Level two
description(10)</description>
> </descriptions>
>
> =============
> xslt stylesheet
> =============
> <?xml version="1.0" encoding="UTF-8"?>
> <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
> <xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes"/>
>
> <xsl:template match="/">
> <xsl:apply-templates select="/table/row" mode="descriptions"/>
> </xsl:template>
>
> <xsl:template match="table/row" mode="descriptions">
> <xsl:variable name="indent"
>
select="string-length(substring-before(cell,substring(normalize-space(cell),
1,1)))"/>
> <xsl:variable name="row" select="position()"/>
> <descriptions>
> <xsl:choose>
> <xsl:when test="$indent = 0">
> <description row="{$row}">
> <xsl:attribute name="level">
> <xsl:value-of select="1"/>
> </xsl:attribute>
> <xsl:value-of select="cell"/>
> </description>
> </xsl:when>
> <xsl:otherwise>
> <xsl:apply-templates
>
select="preceding::cell[(string-length(substring-before(.,substring(normaliz
e-space(.),1,1))))
> = 0][1]" mode="descHierarchy">
> <xsl:with-param name="endAnchor" select="generate-id(cell)"/>
> <xsl:with-param name="indent" select="$indent"/>
> <xsl:with-param name="row" select="$row"/>
> </xsl:apply-templates>
> </xsl:otherwise>
> </xsl:choose>
> </descriptions>
> </xsl:template>
>
> <xsl:template match="cell" mode="descHierarchy">
> <xsl:param name="endAnchor"/>
> <xsl:param name="indent" select="0"/>
> <xsl:param name="row" select="1"/>
> <xsl:for-each select=".|following::cell[generate-id() =
> $endAnchor]|following::cell[following::cell[generate-id() =
$endAnchor]][
>
(string-length(substring-before(.,substring(normalize-space(.),1,1))))
<
> $indent]" >
> <xsl:variable name="thisIndent"
>
select="(string-length(substring-before(.,substring(normalize-space(.),1,1))
))"/>
> <xsl:choose>
> <xsl:when test="not(following::cell[
following::cell[generate-id() =
> $endAnchor] ][
>
(string-length(substring-before(.,substring(normalize-space(.),1,1))))
=
> $thisIndent ])">
> <description row="{$row}">
> <xsl:attribute name="level">
> <xsl:value-of select="position()"/>
> </xsl:attribute>
> <xsl:value-of select="."/>
> </description>
> </xsl:when>
> </xsl:choose>
> </xsl:for-each>
> </xsl:template>
>
> </xsl:stylesheet>
Hi Grainne,
It seems to me that the following solution is simpler. It uses a
recursively called named template to generate the descriptions, and
also the fact that the indent for every next level is 3:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="/*/row">
<descriptions>
<xsl:call-template name="genDescription">
<xsl:with-param name="pNode" select="."/>
<xsl:with-param name="pRow" select="position()"/>
<xsl:with-param name="pLevel"
select="string-length(substring-before(cell,
substring(normalize-space(cell),
1,1
)
)
)
div 3 + 1"/>
</xsl:call-template>
</descriptions>
</xsl:for-each>
</xsl:template>
<xsl:template name="genDescription">
<xsl:param name="pNode" select="/.."/>
<xsl:param name="pRow" />
<xsl:param name="pLevel"/>
<xsl:if test="$pLevel > 1">
<xsl:variable name="vindentParent" select="($pLevel - 2) * 3"/>
<xsl:variable name="vParent"
select="preceding-sibling::row
[not(translate(substring(cell, 1, $vindentParent),
' ', ''
)
)
and substring(cell, $vindentParent + 1, 1) != ' '
]
[1]"/>
<xsl:call-template name="genDescription">
<xsl:with-param name="pNode" select="$vParent"/>
<xsl:with-param name="pRow" select="$pRow"/>
<xsl:with-param name="pLevel" select="$pLevel - 1"/>
</xsl:call-template>
</xsl:if>
<description row="{$pRow}" level="{$pLevel}">
<xsl:value-of select="$pNode/cell"/>
</description>
</xsl:template>
</xsl:stylesheet>
When applied on your source xml document, the following correct result
is produced:
<descriptions>
<description row="1" level="1">Level one
description(1)</description>
</descriptions>
<descriptions>
<description row="2" level="1">Level one
description(1)</description>
<description row="2" level="2"> Level two
description(2)</description>
</descriptions>
<descriptions>
<description row="3" level="1">Level one
description(1)</description>
<description row="3" level="2"> Level two
description(2)</description>
<description row="3" level="3"> Level three
description(3)</description>
</descriptions>
<descriptions>
<description row="4" level="1">Level one
description(1)</description>
<description row="4" level="2"> Level two
description(4)</description>
</descriptions>
<descriptions>
<description row="5" level="1">Level one
description(1)</description>
<description row="5" level="2"> Level two
description(4)</description>
<description row="5" level="3"> Level three
description(5)</description>
</descriptions>
<descriptions>
<description row="6" level="1">Level one
description(6)</description>
</descriptions>
<descriptions>
<description row="7" level="1">Level one
description(6)</description>
<description row="7" level="2"> Level two
description(7)</description>
</descriptions>
<descriptions>
<description row="8" level="1">Level one
description(6)</description>
<description row="8" level="2"> Level two
description(7)</description>
<description row="8" level="3"> Level three
description(8)</description>
</descriptions>
<descriptions>
<description row="9" level="1">Level one
description(6)</description>
<description row="9" level="2"> Level two
description(7)</description>
<description row="9" level="3"> Level three
description(8)</description>
<description row="9" level="4"> Level four
description(9)</description>
</descriptions>
<descriptions>
<description row="10" level="1">Level one
description(6)</description>
<description row="10" level="2"> Level two
description(10)</description>
</descriptions>
=====
Cheers,
Dimitre Novatchev.
http://fxsl.sourceforge.net/ -- the home of FXSL
__________________________________________________
Do you Yahoo!?
Yahoo! Mail Plus - Powerful. Affordable. Sign up now.
http://mailplus.yahoo.com
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
|