"Grainne Reilly" <greilly1(_at_)attbi(_dot_)com> wrote in message
news:5(_dot_)1(_dot_)0(_dot_)14(_dot_)0(_dot_)20021208153308(_dot_)00b38b08(_at_)mail(_dot_)attbi(_dot_)com(_dot_)(_dot_)(_dot_)
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