Linga Murthy C S
Linga Murthy C S

Reputation: 5432

XSLT templates' ambiguity clarification

When ran the following input XML

<root>
    <value>false</value>
    <value>true</value>
</root>

against the following XSLT:

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

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

<xsl:template match="value">
    <true_value/>
</xsl:template>

<xsl:template match="value[. = 'false']">
    <false_value/>
</xsl:template>

</xsl:stylesheet>

I get value element with 'false' as its content changed to false_value.. and all other value elements are turned into true_value. Output:

<?xml version="1.0" encoding="utf-8"?>
<root>
   <false_value/>
   <true_value/>
</root>

But only when I change the template match to root/value do I get ambiguous template warning.

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

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

<xsl:template match="root/value">
    <true_value/>
</xsl:template>

<xsl:template match="root/value[. = 'false']">
    <false_value/>
</xsl:template>

</xsl:stylesheet>

Please help me by explaining what difference does addition of root to the xpath in xsl:template's @match makes that I get this warning.(Ambiguous rule match for /root[1]/value[1])

Upvotes: 6

Views: 767

Answers (2)

Ihe Onwuka
Ihe Onwuka

Reputation: 477

In general the default priorities are meant to indicate the specificity of the match pattern in the template rule. The match pattern "value" is less specific than "root/value" which only matches a value element with a root parent hence root/value has a higher default priority.

That default priority (0.5) happens to be the same as that of a match pattern that features a predicate (note that root/value can also be written as value[parent::root]) and that caused your template conflict.

You are also vulnerable to template conflict on your first template pattern which is the identity template which (for example) will conflict with a template that matched *. Note that when such conflicts are found it is permissible for an XSLT processor to fail rather than try to choose based on the position of the respective templates

If the identity transform is imported from it's stylesheet, needless duplication is eliminated and conflicts are mitigated because templates from imported stylesheets have a lower precedence than templates in the importing stylesheet.

Upvotes: 1

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

Reputation: 22617

Your result is due to implicit template priorities. You can explicitly specify a priority on any template:

<xsl:template match="foo" priority="2"/>

But in most cases, you do not state explicitly what priority you would like a template to adopt - and that's where the default priorities step in. If there is conflict between templates, that is, if an input node matches several templates, XSLT defines a conflict resolution procedure that makes use of the default priorities.

The two templates that cause the processor to issue a warning:

<xsl:template match="root/value">

and

<xsl:template match="root/value[. = 'false']">

have the same default priority (0.5). You would think that the match pattern match="root/value[. = 'false']" is more specific than match="root/value", but as far as the specification is concerned, it is not - they have exactly the same priority.

And that is why an ambiguous rule match is reported. An ambiguous rule match is a situation where the conflict cannot be resolved with either the explicit or implicit priorities. As a last resort, the last template is chosen.

To complete this thought experiment, change the order of templates to

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

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

<xsl:template match="root/value[. = 'false']">
    <false_value/>
</xsl:template>

<xsl:template match="root/value">
    <true_value/>
</xsl:template>

</xsl:stylesheet>

And the result will be (see it online here):

<?xml version="1.0" encoding="utf-8"?>
<root>
   <true_value/>
   <true_value/>
</root>

As you can see, for both value elements, the last template is chosen.

Why, then, does adding root/ to a template match result in a warning about template ambiguity?

The specific change you make is from

<xsl:template match="value">

to

<xsl:template match="root/value">

This changes the default priority (as discussed above) of the template. The default priority of value is 0, the default priority of root/value is 0.5. Only in the second case a conflict will arise, because the default priority of the other template is also 0.5.

Adding root/ to the second template:

<xsl:template match="root/value[. = 'false']">

does not change anything, the default priority remains 0.5.


See the relevant part of the XSLT specification. Caveat: the default priorities there are not exactly easy to read.


All priorities:

<xsl:template match="value">                        0
<xsl:template match="value[. = 'false']">         0.5
<xsl:template match="root/value">                 0.5
<xsl:template match="root/value[. = 'false']">    0.5

Upvotes: 12

Related Questions