Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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])

like image 387
Lingamurthy CS Avatar asked Nov 22 '14 15:11

Lingamurthy CS


People also ask

What is the difference between call template and apply template in XSLT?

With <xsl:apply-templates> the current node moves on with every iteration, whereas <xsl:call-template> does not change the current node.

What is the use of apply templates in XSLT?

The <xsl:apply-templates> element applies a template to the current element or to the current element's child nodes. If we add a "select" attribute to the <xsl:apply-templates> element, it will process only the child elements that matches the value of the attribute.

Why XSLT is template based?

XSLT provides Xpath to locate elements/attribute within an XML document. So it is more convenient way to traverse an XML document rather than a traditional way, by using scripting language. XSLT is template based. So it is more resilient to changes in documents than low level DOM and SAX.

How does xsl template match work?

The <xsl: template match=”/”> is an unnamed template and make use of apply-templates to specify the selected node of the input document. It is made mandatory unless it has a name attribute. In layman terms, we can say that an attribute is a pattern that defines which nodes to have and what attributes to apply to them.


1 Answers

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
like image 102
Mathias Müller Avatar answered Sep 21 '22 02:09

Mathias Müller