xsl-list
[Top] [All Lists]

Re: Hierarchy based on indentation (long)

2002-12-08 15:49:16

"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))))
&lt;
$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



<Prev in Thread] Current Thread [Next in Thread>