Subject: Re: [xsl] Constructing multi-level lists - any better than this? From: "Andrew Welch" <andrew.j.welch@xxxxxxxxx> Date: Sun, 16 Sep 2007 13:52:38 +0100 |
On 9/15/07, Michael M|ller-Hillebrand <mmh@xxxxxxxxxxxxx> wrote: > 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> Hi Michael, Here's an XSLT 2.0 version: <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="levels"> <xsl:copy> <xsl:for-each-group select="*[not(position() = last())]" group-starting-with="*[self::p]"> <p/> <ul level="1"> <xsl:for-each-group select="current-group()" group-by="name()"> <xsl:choose> <xsl:when test="self::li1"> <xsl:apply-templates select="current-group()"/> </xsl:when> <xsl:when test="self::li2"> <ul level="2"> <xsl:apply-templates select="current-group()"/> </ul> </xsl:when> </xsl:choose> </xsl:for-each-group> </ul> </xsl:for-each-group> <p/> </xsl:copy> </xsl:template> <xsl:template match="li1|li2"> <li tag="{name()}" pos="{position()}"> <xsl:apply-templates/> </li> </xsl:template> </xsl:stylesheet> cheers -- Andrew Welch http://andrewjwelch.com Kernow: http://kernowforsaxon.sf.net/
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
RE: [xsl] Constructing multi-level , Geert Josten | Thread | Re: [xsl] Constructing multi-level , Abel Braaksma |
[xsl] [XSL] XSL Browser Integration, Alain | Date | RES: [xsl] Hide and show rows, Jonathan Dias |
Month |