RE: [xsl] Constructing multi-level lists - any better than this?

Subject: RE: [xsl] Constructing multi-level lists - any better than this?
From: "Geert Josten" <geert.josten@xxxxxxxxxxx>
Date: Sat, 15 Sep 2007 19:07:14 +0200
Hi,

I would have done something like this (pseudo-code):

<template match="p">
	<copy />
</template>

<template match="l1|l2">
	<if test="preceding-sibling::*[1][self::p]">
		<ul>
			<apply-templates select="." mode="level1" />
		</ul>
	</if>
</template>

<template match="*" mode="level1">
	<!-- terminate continuation if not l1 or l2 -->
</template>

<template match="l1" mode="level1">
	<copy />
	<apply-templates select="following-sibling::*[1]" mode="level1" />
</template>

<template match="l2" mode="level1">
	<if test="preceding-sibling::*[1][self::p or self::l1]">
		<ul>
			<apply-templates select="." mode="level2" />
		</ul>
	</if>
	<apply-templates select="following-sibling::*[1]" mode="level1" />
</template>

<template match="*" mode="level2">
	<!-- terminate continuation if not l2 -->
</template>

<template match="l2" mode="level2">
	<copy />
	<apply-templates select="following-sibling::*[1]" mode="level2" />
</template>

But it is limited by recursion depth. On the other hand, it doesn't require
XSLT 2.0.

Kind regards,
Geert

>


Drs. G.P.H. Josten
Consultant



Daidalos BV
Source of Innovation
Hoekeindsehof 1-4
2665  JZ  Bleiswijk
Tel.: +31 (0) 10 850 1200
Fax: +31 (0) 10 850 1199
www.daidalos.nl
KvK 27164984


De informatie - verzonden in of met dit emailbericht - is afkomstig van
Daidalos BV en is uitsluitend bestemd voor de geadresseerde. Indien u dit
bericht onbedoeld hebt ontvangen, verzoeken wij u het te verwijderen. Aan dit
bericht kunnen geen rechten worden ontleend.


> From: Michael M|ller-Hillebrand [mailto:mmh@xxxxxxxxxxxxx]
> Sent: zaterdag 15 september 2007 18:44
> To: XSL-List
> Subject: [xsl] Constructing multi-level lists - any better than this?
>
> Hello,
>
> I have a working stylesheet, but it uses modes when I think
> it could be a bit slimmer and maybe more flexible.
>
> The task is to create list containers (<ul>) around list
> elements in a flat element tree. I tried to follow Jeni
> Tennison's advice for constructing hierarchies
> <http://jenitennison.com/xslt/hierarchies-
> out.xml> and also evaluated xsl:for-each-group, but the
> latter seems not to work very well for a stylesheet in push
> mode (no changes to the element order).
>
> To correctly group the <li1>s and <li2>s (even in the third
> case) in this example I successfully used the XSL below. I
> found no good enough example in the FAQs, so I dare to ask,
> whether there is a more elegant solution. (I simplified the
> case, in reality there are multiple elements that are either
> level 1 or level 2 list elements, and I do not use
> starts-with() to detect element names.)
>
> Any advice is greatly appreciated!
>
> - Michael M|ller-Hillebrand
>
> <?xml version="1.0" encoding="UTF-8"?>
> <levels>
> <p/>
> <li1>1</li1>
> <li1>2</li1>
> <li1>3</li1>
> <li1>4</li1>
> <p/>
> <li1>5</li1>
> <li1>6</li1>
> <li2>7.1</li2>
> <li2>7.2</li2>
> <p/>
> <li2>8.1</li2>
> <li2>8.2</li2>
> <li1>9</li1>
> <li1>10</li1>
> <p/>
> </levels>
>
> ---------
>
> <xsl:stylesheet version="2.0"
>                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
> <xsl:strip-space elements="*"/>
> <xsl:output method="xml" indent="yes" />
>
> <!-- Root element -->
> <xsl:template match="levels">
>    <xsl:copy>
>      <xsl:apply-templates />
>    </xsl:copy>
> </xsl:template>
>
> <!-- Keys to identify list elements after first element -->
> <xsl:key name="list12-other"
>    match="*[self::*[starts-with(name(), 'li')]
>      and preceding-sibling::*[1][starts-with(name(), 'li')]]"
>    use="generate-id(
>      preceding-sibling::*[
>        starts-with(name(), 'li')
>        and not(preceding-sibling::*[1][starts-with(name(), 'li')])
>      ][1])" />
>
> <xsl:key name="list2-other"
>    match="li2[preceding-sibling::*[1][self::li2]]"
>    use="generate-id(
>      preceding-sibling::li2[
>        not(preceding-sibling::*[1][self::li2])
>      ][1])" />
>
> <!-- List 1 Container-->
> <xsl:template match="*[starts-with(name(), 'li')
>    and not(preceding-sibling::*[1][starts-with(name(), 'li')])]"
> priority="1">
>    <ul level="1">
>      <xsl:apply-templates mode="list1"
>        select=". | key('list12-other', generate-id())" />
>    </ul>
> </xsl:template>
> <!-- List 1 elements -->
> <xsl:template match="li1" mode="list1">
>    <li tag="{name()}" pos="{position()}">
>      <xsl:apply-templates />
>    </li>
> </xsl:template>
>
> <!-- or List 2 Container-->
> <xsl:template match="li2[not(preceding-sibling::*[1][self::li2])]"
>    mode="list1" priority="1">
>    <ul level="2">
>      <xsl:apply-templates mode="list2"
>        select=". | key('list2-other', generate-id())" />
>    </ul>
> </xsl:template>
> <!-- List 2 elements -->
> <xsl:template match="li2" mode="list2">
>    <li tag="{name()}" pos="{position()}">
>      <xsl:apply-templates />
>    </li>
> </xsl:template>
>
> <!-- skip list elements when matched outside list -->
> <xsl:template match="li2" mode="list1"/> <xsl:template
> match="*[starts-with(name(), 'li')]"/>
>
> <!-- all other nodes -->
> <xsl:template match="node()">
>    <xsl:copy>
>      <xsl:apply-templates />
>    </xsl:copy>
> </xsl:template>
>
> </xsl:stylesheet>

Current Thread