[xsl] Flat list to nested list,grouping

Subject: [xsl] Flat list to nested list,grouping
From: "Kjellaug Johansen" <kjellaug.johansen@xxxxx>
Date: Thu, 31 Mar 2011 15:10:43 +0200
I've been trying for a long time, but I can't figure it out. The problem
is making structured lists out of a flat xml. I've got it almost right.
The list's first level is ok, but the second level only prints out the
first point. The third level is not printed in my output at all.

I guess there is something to do with the key name kListGroup1.

PS: This is XSL 1.0
PS: There can be a various number of levels in the list from time to
time.

Anyone with a solution? Thanks!

Kjellaug.

The result I want:

<myresult>
	<Title>Numericlist</Title>
	<List level="1">
		<Pkt>
			<A>List level1, Point 1</A>
		</Pkt>
		<Pkt>
			<A>List level1, Point 2</A>
			<A>New paragraph in point 2</A>
			<A>Another paragraph in point 2</A>
		</Pkt>
		<Pkt>
			<A>List level1, Point 3</A>
			<List level="2">
				<Pkt>
					<A>List level2, point 1</A>
				</Pkt>
				<Pkt>
					<A>List level2, point 2</A>
					<A>New paragraph under point 2
list 2</A>
				</Pkt>
				<Pkt>
					<A>List level2, point 3</A>
					<List level="3">
						<Pkt>
							<A>List level3,
point 1</A>
							<A>New paragraph
under point 1 							list
3</A>
						</Pkt>
				</Pkt>
			</Liste>
		</Pkt>
		<Pkt>
			<A>List level1, point 4</A>
		</Pkt>
	</Liste>
</myresult>

Here's my input xml:

<myxml>
	<p val="X">Div elements</p>
	<p val="Tit">Numericlist</p>
	<p val="LNum1">List level1, Point 1</p>
	<p val="LNum1">List level1, Point 2</p>
	<p val="LPara1">New paragraph in point 2</p>
	<p val="LPara1">Another paragraph in point 2</p>
	<p val="LNum1">List level1, Point 3</p>
	<p val="LNum2">List level2, point 1</p>
	<p val="LNum2">List level2, point 2</p>
	<p val="LPara2">New paragraph under point 2 list 2</p>
	<p val="LNum2">List level2, point 3</p>
	<p val="LNum3">List level3, point 1</p>
	<p val="LPara3">New paragraph under point 1 list 3</p>
	<p val="LNum1">List level1, point 4</p>
	<p val="Y">Other elements</p>
</myxml>

This is my xsl:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format";>
	<xsl:output method="xml" indent="yes"/>
	<xsl:key name="kListGroup1" match="p[contains(@val,'LNum')]"
use="generate-id(preceding-sibling::node()[self::p[contains(@val,'LNum')
or contains(@val,'LPara')]][1])"/> <!--This is probably not correct-->
	<xsl:key name="kListGroup2" match="p[contains(@val,'LNum')]"
use="generate-id(preceding-sibling::node()[not(self::p[contains(@val,'LN
um') or contains(@val,'LPara')])][1])"/> <!--This works fine-->
	<xsl:template match="myxml">
		<xsl:element name="myresult">
			<xsl:apply-templates/>
		</xsl:element>
	</xsl:template>
	<xsl:template match="p[@val='X']"/>
	<xsl:template match="p[@val='Y']"/>
	<xsl:template match="p[@val='Tit']">
		<xsl:element name="Title">
			<xsl:value-of select="."/>
		</xsl:element>
		<xsl:if test="following::p[1]/@val='LNum1'">
<!--Discovers the numeric list-->
			<xsl:apply-templates
select="following-sibling::node()[1]" mode="liste"/>
		</xsl:if>
	</xsl:template>
	<xsl:template match="p" mode="liste">
		<xsl:variable name="listlevel"
select="substring(@val,5)"/>
		<xsl:element name="List">
			<xsl:attribute name="level"><xsl:value-of
select="$listlevel"/></xsl:attribute>
			<xsl:apply-templates mode="listgroup"
select="key('kListGroup2',
generate-id(preceding-sibling::p[1]))[substring(@val,5) = $listlevel]"/>
<!--This works fine, lists all at level 1-->
		</xsl:element>
	</xsl:template>
	<xsl:template match="p" mode="innerlist">
		<xsl:variable name="listlevel"
select="substring(@val,5)"/>
		<xsl:element name="List">
			<xsl:attribute name="level"><xsl:value-of
select="$listlevel"/></xsl:attribute>
			<xsl:apply-templates mode="listgroup"
select="key('kListGroup1',
generate-id(preceding-sibling::p[1]))[substring(@val,5) = $listlevel]"/>
<!--This DOESN'T work, lists only the first element at the second
level-->
		</xsl:element>
	</xsl:template>
	<xsl:template match="p" mode="para">
		<xsl:element name="A">
			<xsl:value-of select="."/>
		</xsl:element>
	</xsl:template>
	<xsl:template match="p" mode="listgroup"><!--the logic of the
list-content goes here, works fine-->
		<xsl:element name="Pkt">
			<xsl:element name="A">
				<xsl:value-of select="."/>
			</xsl:element>
			<xsl:variable name="level">
				<xsl:value-of
select="substring(@val,5)"/>
			</xsl:variable>
			<xsl:variable name="vNextAvs"
select="following-sibling::p[1][contains(@val,'LPara')][1]"/>
			<xsl:if test="$vNextAvs"> <!--if there are more
paragraphs in the listpoint, do this....it works fine-->
				<xsl:apply-templates
select="following-sibling::p[contains(@val,'LPara')][generate-id(precedi
ng-sibling::p[contains(@val,'LNum')][1])
					 = generate-id(current())]"
mode="para"/>
			</xsl:if>
			<xsl:variable name="vNext"
select="following-sibling::p[1][contains(@val,'LNum')][number(substring(
@val,5) > number($level))][1]"/>
			<xsl:if test="$vNext">
				<xsl:apply-templates
select="following-sibling::node()[1]" mode="innerlist"/>
			</xsl:if>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>

Current Thread