9

I thought I saw a mistake in an answer to this question, and pointed it out. I was told I was incorrect, and my answer was later deleted.

I still don't see how I was wrong. Therefore, I am posting here and hoping someone can explain my misunderstanding to me.

The answer I responded to explained the use of apply-templates. It contained the following XML and XSL, describing how the templates would be matched:

<!-- sample XML snippet -->
<xml>
  <foo /><bar /><baz />
</xml>

<!-- sample XSLT snippet -->
<xsl:template match="xml">
  <xsl:apply-templates select="*" /> <!-- three nodes selected here -->
</xsl:template>

<xsl:template match="foo"> <!-- will be called once -->
  <xsl:text>foo element encountered</xsl:text>
</xsl:template>

<xsl:template match="xml/*"> <!-- will be called twice -->
  <xsl:text>other element countered</xsl:text>
</xsl:template>

My comment was that the last template in should be:

<xsl:template match="*"> <!-- will be called twice -->
  <xsl:text>other element countered</xsl:text>
</xsl:template>

because the current node is already <xml>

I was told:

No, xml/* is a pattern that matches child elements of an element with the name xml.

Testing the original answer

However, with this XML:

<xml>
  <foo /><bar /><baz />
</xml>

And this XSL stylesheet (filling out the snippet above):

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

<xsl:template match="xml">
  <xsl:apply-templates select="*" /> <!-- three nodes selected here -->
</xsl:template>

<xsl:template match="foo"> <!-- will be called once -->
  <xsl:text>foo element encountered.&#xa;</xsl:text>
</xsl:template>

<xsl:template match="xml/*"> <!-- will be called twice -->
  <xsl:text>other element countered.&#xa;</xsl:text>
</xsl:template>

</xsl:stylesheet>

I get:

other element countered.
other element countered.
other element countered.

Testing my 'corrected' version

If I replace the last template with:

<xsl:template match="*"> <!-- will be called twice -->
  <xsl:text>other element countered.&#xa;</xsl:text>
</xsl:template>

as per my answer I get:

foo element encountered.
other element countered.
other element countered.

which would appear to be correct.

I hope my question doesn't break any guidelines, but I can't see that I'm wrong and am hoping someone can explain it more fully.

PS. I'm afraid my original response on the other question was posted as an answer, not a comment, as I don't have enough points to post comments yet. I wasn't sure what the best thing was to do...

Community
  • 1
  • 1
Ben L
  • 1,302
  • 11
  • 23
  • 1
    Thanks for finding that stupid mistake of mine, it's fixed now in the other thread. This went unnoticed by everybody else who looked at the answer throughout the years. – Tomalak Apr 08 '13 at 04:37

2 Answers2

7

This is correct, according to the rules on the default priority of templates. A template matching foo has default priority 0, one matching * has default priority -0.5, but one matching xml/* has default priority 0.5. The xml/* template is considered more specific than the foo one, so it wins when either could match.

So you were right that the template's match expression needed to be * rather than xml/*, but not for the right reason - an xml/* template can match for an apply-templates select="*" when the current node is xml, and it will apply to any of those selected elements (since they are all children of xml) except where there is another template with an explicit priority greater than 0.5 that can take precedence.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
4

I was the person who said you were incorrect in the other thread, and after looking at the matter more closely, I can see that you were correct in pointing out that Tomalak made a mistake, but not for the reasons you gave (if I'm understanding your comment correctly). match="xml/*" does match child nodes of the <xml> element, whether or not the current context was the <xml> node when apply-templates was called. In a match="" expression, the "current node" is the node on which templates are being applied, not the context where apply-templates was invoked, so in this template the current node would be foo, bar, and baz. You can observe from your own experimentation above that xml/* does succeed in matching child elements of xml, but in fact matches them more than Tomalak says it will.

The problem with match="xml/*" is that it's too specific and has the opposite effect of what I think Tomalak intended. It seems he meant for this to be a catch-all for children of xml that were not matched otherwise, but as Ian Roberts explains, this template winds up with a higher priority than the foo template, and catches all of the children of the xml node.

I know it's frustrating right now not being able to write comments of your own, but soon enough you will. And it is acceptable to create your own new question to ask a question about another thread. You just shouldn't create answers to comment on other answers.

JLRishe
  • 99,490
  • 19
  • 131
  • 169