RE: [xsl] Nested lists

Subject: RE: [xsl] Nested lists
From: "Rick Quatro" <rick@xxxxxxxxxxxxxx>
Date: Thu, 31 Oct 2013 21:05:30 -0400
Hi Heiko,

Thanks. I am using MSXML so I have to do this with XSLT 1.0.

Rick

-----Original Message-----
From: Heiko Niemann [mailto:kontakt@xxxxxxxxxxxxxxxx] 
Sent: Thursday, October 31, 2013 8:13 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: [xsl] Nested lists

Hi,

using XSLT 2.0 this does what you want (somebody might improve this since
grouping feels still new to me):


<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="2.0">
  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:template match="/">
    <body>
      <xsl:for-each-group select="/body/*"
group-starting-with="*[not(@class = ('listitembullets',
'listitemindented'))]">
        <xsl:copy-of select="."/>
        <xsl:call-template name="list"/>
      </xsl:for-each-group>
    </body>
  </xsl:template>
  <xsl:template name="list">
    <xsl:if test="current-group()[2]">
      <ul>
        <xsl:variable name="cl" select="current-group()[2]/@class"/>
        <xsl:for-each-group select="current-group() except ."
group-starting-with="p[@class = $cl]">
          <li>
            <xsl:value-of select="text()"/>
            <xsl:call-template name="list"/>
          </li>
        </xsl:for-each-group>
      </ul>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>


Output:


<body>
  <p class="listintro">ListItem_Bullets:</p>
  <ul>
    <li>. Item 1</li>
    <li>. Item 2</li>
    <li>. Item 3</li>
  </ul>
  <p class="normal">Paragraph within a list.</p>
  <ul>
    <li>. Item 4</li>
    <li>. Item 5
      <ul>
        <li>. Item 1 indented</li>
        <li>. Item 2 indented</li>
      </ul>
    </li>
    <li>. Item 6</li>
  </ul>
  <h2>Indented list starts here:</h2>
  <ul>
    <li>. Item 1 indented</li>
    <li>. Item 2 indented</li>
    <li>. Item 3 indented</li>
  </ul>
  <p class="normal">Paragraph within a list.</p>
  <ul>
    <li>. Item 4 indented</li>
    <li>. Item 5 indented</li>
    <li>. Item 6 indented</li>
  </ul>
</body>


Heiko


> Hi,
>
> I have this for my input XML:
>
> <body>
>   <p class="listintro">ListItem_Bullets:</p>
>   <p class="listitembullets">&#8226; Item 1</p>
>   <p class="listitembullets">&#8226; Item 2</p>
>   <p class="listitembullets">&#8226; Item 3</p>
>   <p class="normal">Paragraph within a list.</p>
>   <p class="listitembullets">&#8226; Item 4</p>
>   <p class="listitembullets">&#8226; Item 5</p>
>   <p class="listitemindented">&#8226; Item 1 indented</p>
>   <p class="listitemindented">&#8226; Item 2 indented</p>
>   <p class="listitembullets">&#8226; Item 6</p>
>   <h2>Indented list starts here:</h2>
>   <p class="listitemindented">&#8226; Item 1 indented</p>
>   <p class="listitemindented">&#8226; Item 2 indented</p>
>   <p class="listitemindented">&#8226; Item 3 indented</p>
>   <p class="normal">Paragraph within a list.</p>
>   <p class="listitemindented">&#8226; Item 4 indented</p>
>   <p class="listitemindented">&#8226; Item 5 indented</p>
>   <p class="listitemindented">&#8226; Item 6 indented</p> </body>
>
> I want to get this:
>
> <body>
>   <p class="listintro">ListItem_Bullets:</p>
>   <ul>
>      <li>Item 1</li>
>      <li>Item 2</li>
>      <li>Item 3</li>
>   </ul>
>   <p class="normal">Paragraph within a list.</p>
>   <ul>
>      <li>Item 4</li>
>      <li>Item 5</li>
>      <ul>
>        <li>Item 1 indented</li>
>        <li>Item 2 indented</li>
>      </ul>
>      <li>Item 6</li>
>   </ul>
>   <h2>Indented list starts here:</h2>
>   <ul>
>      <li>. Item 1 indented</li>
>      <li>. Item 2 indented</li>
>      <li>. Item 3 indented</li>
>   </ul>
>   <p class="normal">Paragraph within a list.</p>
>   <ul>
>      <li>. Item 4 indented</li>
>      <li>. Item 5 indented</li>
>      <li>. Item 6 indented</li>
>   </ul>
> </body>
>
> I am actually getting this:
>
> <body>
>   <p class="listintro">ListItem_Bullets:</p>
>   <ul>
>      <li>Item 1</li>
>      <li>Item 2</li>
>      <li>Item 3</li>
>   </ul>
>   <p class="normal">Paragraph within a list.</p>
>   <ul>
>      <li>Item 4</li>
>      <li>Item 5</li>
>   </ul>
>   <h2>Indented list starts here:</h2>
>   <ul>
>      <li>. Item 1 indented</li>
>      <li>. Item 2 indented</li>
>      <li>. Item 3 indented</li>
>   </ul>
>   <p class="normal">Paragraph within a list.</p>
>   <ul>
>      <li>. Item 4 indented</li>
>      <li>. Item 5 indented</li>
>      <li>. Item 6 indented</li>
>   </ul>
> </body>
>
> The problem is that I have some different list items represented by 
> the various class attributes. I tried to get cute with keys. It works 
> for all of the lists except the "listitemindented" lists. Here is my 
> stylesheet. Any help will be appreciated. (Forget about the bullets; I 
> can solve that
> separately.)
>
> <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet 
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>     xmlns:xhtml="http://www.w3.org/1999/xhtml";
>     xmlns="http://www.w3.org/1999/xhtml";
>     exclude-result-prefixes="xhtml xsl"
>     version="1.0">
>
>     <xsl:key name="orderedlists" match="xhtml:p[@class='listitem1' or 
> @class='listitem2']"
> use="generate-id((..|preceding-sibling::*[not(self::xhtml:p[@class='li
> stitem 1' or @class='listitem2'])][1])[last()])"/>
>     <xsl:key name="unorderedlists-plain"
> match="xhtml:p[@class='listitemunordered']"
> use="generate-id((..|preceding-sibling::*[not(self::xhtml:p[@class='li
> stitem
> unordered'])][1])[last()])"/>
>
>     <xsl:key name="unorderedlists"
> match="xhtml:p[@class='listitembullets']"
> use="generate-id((..|preceding-sibling::*[not(self::xhtml:p[@class='li
> stitem
> bullets'])][1])[last()])"/>
>     <xsl:key name="unorderedlists-indented"
> match="xhtml:p[@class='listitemindented']"
> use="generate-id((..|preceding-sibling::*[not(self::xhtml:p[@class='li
> stitem
> indented'])][1])[last()])"/>
>
>     <xsl:output indent="yes"/>
>
>     <!--this is a construct whose children need to be grouped in 
> separate
> lists-->
>     <xsl:template match="xhtml:body">
>         <body>
>             <xsl:apply-templates mode="group"
> select=".|*[not(self::xhtml:p[@class='listitem1' or @class='listitem2' 
> or @class='listitemunordered' or @class='listitembullets' or 
> @class='listitemindented'])]"/>
>         </body>
>     </xsl:template>
>
>     <xsl:template mode="group" match="*">
>         <xsl:apply-templates select="self::*[not(self::xhtml:body)]"/>
>         <xsl:if test="key('orderedlists',generate-id(.))">
>             <ol
> start="{substring-before(following-sibling::*/node(),'.')}">
>                 <xsl:apply-templates
> select="key('orderedlists',generate-id(.))"/>
>             </ol>
>         </xsl:if>
>         <xsl:if test="key('unorderedlists-plain',generate-id(.))">
>             <dl>
>                 <xsl:apply-templates
> select="key('unorderedlists-plain',generate-id(.))"/>
>             </dl>
>         </xsl:if>
>         <xsl:if test="key('unorderedlists-indented',generate-id(.))">
>             <ul>
>                 <xsl:apply-templates
> select="key('unorderedlists-indented',generate-id(.))"/>
>             </ul>
>         </xsl:if>
>         <xsl:if test="key('unorderedlists',generate-id(.))">
>             <ul>
>                 <xsl:apply-templates
> select="key('unorderedlists',generate-id(.))"/>
>             </ul>
>         </xsl:if>
>     </xsl:template>
>
>     <!--the following template rules need no knowledge of grouping-->
>
>     <xsl:template match="xhtml:p[@class='listitem1' or 
> @class='listitem2']">
>         <li><xsl:apply-templates/></li>
>     </xsl:template>
>
>     <xsl:template match="xhtml:p[@class='listitem1' or 
> @class='listitem2']/text()[1]">
>         <xsl:value-of select="substring-after(.,'. ')"/>
>     </xsl:template>
>
>     <xsl:template match="xhtml:p[@class='listitembullets' or 
> @class='listitemindented']">
>         <li><xsl:apply-templates/></li>
>     </xsl:template>
>
>     <xsl:template match="xhtml:p[@class='listitemunordered']">
>         <dd><xsl:apply-templates/></dd>
>     </xsl:template>
>
>     <xsl:template match="xhtml:p[@class='listitembullets']/text()[1]">
>         <xsl:value-of select="substring-after(.,'&#8226; ')"/>
>     </xsl:template>
>
>     <!-- IdentityTransform -->
>     <xsl:template match="/ | @* | node()" name="identity">
>         <xsl:copy><xsl:apply-templates select="@* | node()" /></xsl:copy>
>     </xsl:template>
>
> </xsl:stylesheet>

Current Thread