7

I have a XML document with chapters and nested sections. I am trying to find, for any section, the first second-level section ancestor. That is the next-to-last section in the ancestor-or-self axis. pseudo-code:

<chapter><title>mychapter</title>
  <section><title>first</title>
     <section><title>second</title>
       <more/><stuff/>
     </section>
  </section>
</chapter>

my selector:

<xsl:apply-templates 
    select="ancestor-or-self::section[last()-1]" mode="title.markup" />

Of course that works until last()-1 isn't defined (the current node is the first section).

If the current node is below the second section, i want the title second. Otherwise I want the title first.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Tim
  • 1,013
  • 3
  • 17
  • 36

2 Answers2

5

Replace your xpath with this:

ancestor-or-self::section[position()=last()-1 or count(ancestor::section)=0][1]

Since you can already find the right node in all cases except one, I updated your xpath to also find the first section (or count(ancestor::section)=0), and then select ([1]) the first match (in reverse document order, since we are using the ancestor-or-self axis).

Jason Clark
  • 1,433
  • 1
  • 16
  • 24
  • That works--thanks. After trying your solution, I thought I might simplify it with this, but alas it seems to always select the current section. `ancestor-or-self::d:section[last()-1 or last()][1]` – Tim May 02 '12 at 21:04
  • @Tim `ancestor-or-self::section[last()-boolean(ancestor::section)][1]` – Aleh Douhi May 02 '12 at 21:42
  • @Tim `ancestor-or-self::section[position()>last()-2][1]` :) – Aleh Douhi May 03 '12 at 00:42
2

Here is a shorter and more efficient solution:

(ancestor-or-self::section[position() > last() -2])[last()]

This selects the last of the possibly first two topmost ancestors named section. If there is only one such ancestor, then it itself is the last.

Here is a complete transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="section">
  <xsl:value-of select="title"/>
  <xsl:text> --> </xsl:text>

  <xsl:value-of select=
  "(ancestor-or-self::section[position() > last() -2])[last()]/title"/>
  <xsl:text>&#xA;</xsl:text>
  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

When this transformation is applied on the following document (based on the provided, but added more nested section elements):

<chapter>
    <title>mychapter</title>
    <section>
        <title>first</title>
        <section>
            <title>second</title>
            <more/>
            <stuff/>
        <section>
            <title>third</title>
        </section>
        </section>
    </section>
</chapter>

the correct results are produced:

first --> first
second --> second
third --> second
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431