cainesap
cainesap

Reputation: 110

XSL order of templates for nested elements

I have an XML file like so:

<text>
  <a>foo1</a>
  <a><b>foo2</b></a>
</text>

I have an XSL file designed to process <a> and <a><b> differently with templates 1 and 2:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>

  <xsl:template match="text">
    <xsl:copy>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- t1 -->
  <xsl:template match="a">
    <xsl:element name="keep">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

  <!-- t2 -->
  <xsl:template match="a/b" />

</xsl:stylesheet>

I expected that this would produce:

<text>
  <keep>foo1</keep>
</text>

Because t2 should match and 'ignore' <a><b>foo2</a></b> and I thought that it would take precedence over t1 (both t1 and t2 match <a><b> but t2 is simply later in the XSL). But the output is in fact:

<text>
  <keep>foo1</keep>
  <keep>foo2</keep>
</text>

In fact if I take t2 away the output is the same, so clearly it is not even matching <a><b>. I must be missing something: please can anyone help?

Upvotes: 1

Views: 1124

Answers (1)

Mathias M&#252;ller
Mathias M&#252;ller

Reputation: 22617

What you are looking for is

<xsl:template match="a[b]" />

a template that matches a elements if they have a child element called b.

A template match like

<xsl:template match="a/b" />

does not match a elements, it matches b elements that have an a element as their parent. But in your original stylesheet, the XSLT processor is never prompted to find a matching template for b elements, that's why the code contained in this template (none) is never executed.

With this change, the output is

<?xml version="1.0" encoding="UTF-8"?>
<text>
  <keep>foo1</keep>

</text>

As you can see, it's almost the output you expected. With your XSLT processor, MSXSL, the empty line likely is not even there, because MSXSL strips the tree of whitespace-only nodes. But to have your stylesheet produce consistent results on any processor, the info below is still useful.

To remove the empty line in the output, add xsl:strip-space as a top-level element. Also, save yourself the trouble of typing xsl:element name=... if the element name is known beforehand. The whole stylesheet:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>

    <xsl:strip-space elements="*"/>

  <xsl:template match="text">
    <xsl:copy>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- t1 -->
  <xsl:template match="a">
    <keep>
      <xsl:value-of select="."/>
    </keep>
  </xsl:template>

  <!-- t2 -->
  <xsl:template match="a[b]" />

</xsl:stylesheet>

Upvotes: 1

Related Questions