[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Constructing multi-level lists - any better than

Subject: Re: Constructing multi-level lists - any better than this?
From: "Andrew Welch" <andrew.j.welch@xxxxxxxxx>
Date: Sun, 16 Sep 2007 13:52:38 +0100
Re:  Constructing multi-level lists - any better than
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

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.