xsl-list
[Top] [All Lists]

Re: Identify last node in nested nodeset with same name

2005-06-25 00:04:28
On Thu, 2005-06-23 at 19:26 -0700, Mat Bergman wrote:
I am working with XML data that shares the same
element name for each node set, for example:

<menu name="link1"/>
<menu name="link2">
     <menu name="link2a"/>
     <menu name="link2b"/>
</menu>

My XSL stylesheet transforms this into an HTML
unordered list, like this:
<ul>
<li>link1</li>
<li>link2
     <ul>
     <li>link2a</li>
     <li>link2b</li>
     </ul>
</li>
</ul>

I think this is a good question as asked. I noticed both of the
previous answers started by wrapping the input in a new element and
defining a template for that new node. But sometimes it is necessary
to introduce structure that isn't present in the input around a
sequence of like siblings. Wrapping li s in ul s is a good example.

Here's a solution that uses modes a little much, but tries to make the
point that one could generalize this kind of logic, though it's not
done here.

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- identity -->
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
  </xsl:template>

  <!-- mode change on the first in a sequence of menu siblings -->
  <xsl:template match="menu[not(preceding-sibling::*[1]/self::menu)]">
    <xsl:apply-templates select="." mode="slurp"/>
  </xsl:template>

  <!-- spit this and, recursively, all consecutive menu siblings -->
  <xsl:template match="menu" mode="swish">
    <xsl:apply-templates select="." mode="spit"/>
    <xsl:apply-templates select="following-sibling::*[1]/self::menu"
      mode="swish"/>
  </xsl:template>

  <!-- here we can introduce some new structure -->
  <xsl:template match="menu" mode="slurp">
    <ul>
      <xsl:apply-templates select="." mode="swish"/>
    </ul>    
  </xsl:template>

  <!-- a menu is translated to an li -->
  <xsl:template match="menu" mode="spit">
    <li>
      <xsl:value-of select="@name"/>
      <xsl:apply-templates/>
    </li>
  </xsl:template>

  <!-- ignore any menu item that isn't the first in a series -->
  <xsl:template match="menu"/>

</xsl:stylesheet>

Here's some input:

<root>
  <random>Random text</random>
  <menu name="link1"/>
  <menu name="link2">
    <menu name="link2a"/>
    <menu name="link2b"/>
    <menu name="link2c"/>
    <random>Random text</random>
  </menu>
  <random>Random text</random>
</root>

And here's the output, with spaces added by me:

<root>
  <random>Random text</random>
  <ul>
    <li>link1</li>
    <li>
      link2
      <ul>
        <li>link2a</li>
        <li>link2b</li>
        <li>link2c</li>
      </ul>
      <random>Random text</random>
    </li>
  </ul>
  <random>Random text</random>
</root>

Comments/links welcome.

sdc



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--