2
votes

Assume an XML tree of label and item pairs, each of which contain mark-up requiring transformation:

<list>
   <label>ABC<ptr target="#note-1"/></label>
   <item><ref type="link">123</ref></item>
   <label>DEF<ptr target="#note-2"/></label>
   <item><ref type="link">456</ref></item>
   ...
</list>

(N.B., the mark-up inside the <label> and <item> elements is merely an example. These elements may have an arbitrary amount of mark-up contained within them. So, that nested mark-up will be handled by template rules. Using XSL string functions, etc., to handle this nested mark-up is not an ideal solution since the number of possible tags is large.)

Suppose then the goal is to transform this into an HTML unordered list, like this:

<ul>
   <li>ABC<sup>1</sup> <a href="link1.html">123</a></li>
   <li>DEF<sup>2</sup> <a href="link2.html">456</a></li>
   ...
</ul>

I've been trying to match the <label><item> node pairs with the preceding::sibling axis, e.g.

   <xsl:template match="list[@type='index']/item[preceding-sibling::label]" >
    <li><xsl:value-of select="./preceding-sibling::label[1]"/> <xsl:apply-templates/></li>
   </xsl:template>

But my output emits the content of <label> twice: once before the <li> element (properly transformed) and again inside the <li> element (without transformation), like this:

  <ul>
     ABC<sup>1</sup>
     <li>ABC <a href="link1.html">123</a></li>
     DEF<sup>2</sup>
     <li>DEF <a href="link2.html">456</a></li>
     ...
  </ul>

I understand that the <label> content inside the <li> is not being transformed because I am using <xsl:value-of>. I'm just trying to illustrate the goal I'm trying to achieve of getting the content of both <label> and <item> inside the <li>.

If I add the following template to suppress the second instance of the content from the <label> element:

<xsl:template match="list[@type='index']/label"></xsl:template>

then the redundant set of content from <label> is eliminated, but not the right one. E.g.,

<ul>
     <li>ABC <a href="link1.html">123</a></li>
     <li>DEF <a href="link2.html">456</a></li>
     ...
  </ul>

So, my question is, how do I get the content from <label> and <item> into a single <li> element, while at the same time having that content processed by other templates in my stylesheet?

1

1 Answers

1
votes

This is quite straightforward to do in XSLT:

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

 <xsl:template match="list">
  <ul>
    <xsl:apply-templates select="label"/>
  </ul>
 </xsl:template>
 <xsl:template match="label">
  <li>
   <xsl:value-of select="."/>

   <xsl:variable name="vPos" select=
       "substring-after(ptr/@target, '#note-')"/>
   <sup>
     <xsl:value-of select="$vPos"/>
   </sup>
   <xsl:apply-templates select=
      "following-sibling::*[1][self::item]">
    <xsl:with-param name="pPos" select="$vPos"/>
   </xsl:apply-templates>
  </li>
 </xsl:template>

 <xsl:template match="item">
  <xsl:param name="pPos"/>
   <a href="{@type}{$pPos}.html">
     <xsl:value-of select="."/>
   </a>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document (corrected to be wellformed):

<list>
   <label>ABC<ptr target="#note-1"/></label>
   <item><ref type="link">123</ref></item>
   <label>DEF<ptr target="#note-2"/></label>
   <item><ref type="link">456</ref></item>
</list>

the wanted, correct result is produced:

<ul>
   <li>ABC<sup>1</sup>
      <a href="1.html">123</a>
   </li>
   <li>DEF<sup>2</sup>
      <a href="2.html">456</a>
   </li>
</ul>