Home > OS >  XSLT: numbering elements that can occur at arbitrary places in the tree
XSLT: numbering elements that can occur at arbitrary places in the tree

Time:10-01

I am struggling a bit of XSLT. My (extremely simplified) input looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <p>
        A <note>a</note>
        B <note>b</note>
    </p>
    <p>
        C <note>c</note>
        <!-- note the sneaky quote element -->
        <q>
            D <note>d</note>
        </q>
    </p>
    <p>
        E <note>e</note>
    </p>
</doc>

I would like to convert this to something like:

<body>
    <p>
        A <span >1</span>
      <span >
         <span >1</span>a</span>
        B <span >2</span>
      <span >
         <span >2</span>b</span>
    </p>
    <p>
        C <span >3</span>
      <span >
         <span >3</span>c</span>
        <q>D <span >4</span>
         <span >
            <span >4</span>d</span>
      </q>
    </p>
    <p>
        E <span >5</span>
      <span >
         <span >5</span>e</span>
    </p>
</body>

i.e. the notes are numbered with the order they occur in the document.

The (extremely simplified) code I was using for this is as follows:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes" encoding="utf-8" method="xml"/>
    
    <xsl:template match="p">
        <p>
            <xsl:apply-templates/>
        </p>
    </xsl:template>

    <xsl:template match="q">
        <q>
            <xsl:apply-templates />
        </q>
    </xsl:template>
    
    <xsl:template match="note">
        <xsl:variable name="num" select="count(ancestor::p/preceding-sibling::*//note)   count(preceding-sibling::note)   1"/>
        <span ><xsl:value-of select="$num"/></span>
        <span >
            <span ><xsl:value-of select="$num"/></span>
            <xsl:value-of select="."/>
        </span>
    </xsl:template>
    
    <xsl:template match="/">
        <body>
            <xsl:apply-templates/>
        </body>
    </xsl:template>
</xsl:stylesheet>

That worked okay until someone rudely put a <note> tag inside a <q> tag, at which put the numbering was reset, because I can't figure out how to count the preceding things that might be at a different level from the focus node.

As far as I see it I need to do two things:

  1. count all the notes in preceding paragraphs
  2. count all prior notes in the current paragraph

Step 1 seems to work okay with count(ancestor::p/preceding-sibling::*//note). Step 2 is what I can't figure out.

I'm vaguely aware that position() will give the count of an element within a particular selection, but I can't see how that will work in the context of my document transformation.

CodePudding user response:

In your template matching note, use <xsl:number level="any"/> to get the number you want.

  • Related