XSLT templates' ambiguity clarification - xslt

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

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

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.

Related

XSLT style - pattern matching multiple templates

This is a question about xslt 1.0 (but i've included the general xslt tag as it may apply more widely).
lets say we want to take this xml
<root>
<vegetable>
<carrot colour="orange"/>
<leek colour="green"/>
</vegetable>
</root>
and transform it to cook the vegetables if they are root vegetables so this..
<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="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="carrot">
<xsl:copy>
<xsl:attribute name="cooked">true</xsl:attribute>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="leek">
</xsl:template>
</xsl:stylesheet>
so the xslt recursively processes the data, and when it finds multiple matching templates e.g. leek and carrot, it takes the last one, effectively overriding.
Sometimes accepted answers in this site have this style,
e.g. XSLT copy-of but change values
other answers specifically about multiple matching templates
e.g.
XSLT Multiple templates with same match
state
Having two templates of the same priority that match the same node is an error according to the XSLT specification
It is an error if [the algorithm in section 5.5] leaves more than one
matching template rule. An XSLT processor may signal the error; if it
does not signal the error, it must recover by choosing, from amongst
the matching template rules that are left, the one that occurs last in
the stylesheet.
so....we can avoid this by either using priority or by matching to explicitly excluding the the overlap, something like this.
<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="#* | node()[not(self::carrot) or not(self::leek)]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="carrot">
<xsl:copy>
<xsl:attribute name="cooked">true</xsl:attribute>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="leek">
</xsl:template>
</xsl:stylesheet>
I get the feeling that lots of devs actually simply use the default fallback behaviour and let the processor use the last match, this is similar in style to pattern matching in most functional languages where the 1st match is used.
I also personally am not a fan of priority, it feels a bit like magic numbers, where i have to scan and remember the priority of the pattern matches to work out whats going on.
The approach to explicitly exclude overlaps, seems sensible, but in practice requires complex logic and creates coupling between templates, if i extend/add a new match, i potentially have to amend constrain another.
Is the above a correct summary?
Is there an accepted style (even if it contradicts the spec)?
I think you may be missing the fact that there is no error in the given example, therefore the rule of applying the template that occurs last in the stylesheet is not invoked. You can verify this by switching the order of the templates and observing that the result remains unchanged.
There is no error because the identity transform has a priority of -0.5 while the specific templates have a priority of 0.
Read the entire specification for conflict resolution:
https://www.w3.org/TR/1999/REC-xslt-19991116/#conflict

Replacing particuar String using xsl

I have a block as below.
<rightOperand>.*ifIndedx.*</rightOperand>
But i need to change the above snippet to the below one
<rightOperand>(?i)(?s).*ifIndex.*</rightOperand>
This translation needs to be done only when the right operand starts and ends with the string .*
please provide me some pointers .
You can do this my overriding the identity transform with an extra template just to match the text within rightOperand that matches your criteria
<xsl:template match="rightOperand/text()
[starts-with(., '.*')]
[substring(., string-length(.) - 1, 2) = '.*']">
Note that XSLT 1.0 does not have the ends-with function, which is why there is the extra work to check the ending with substring. If you were using XSLT 2.0 you could simplify this with ends_with though.
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="rightOperand/text()
[starts-with(., '.*')]
[substring(., string-length(.) - 1, 2) = '.*']">
<xsl:text>(?i)(?s)</xsl:text><xsl:copy />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output:
<rightOperand>(?i)(?s).*ifIndedx.*</rightOperand>

XSLT: Match all elements of a namespace except one element

I want to write an XSLT template that matches all elements of one namespace except one element. For example I want to match all elements foo:*, but not foo:bar.
Is that possible to define this in a selector or do I have to write an xsl:if condition within the xsl:template (and how can I test the local name of the element)?
To do this, you can just have a template that matches foo:bar that does nothing with it like so:
<xsl:template match="foo:bar" />
To match other foo elements, you can use a more general template
The XSLT processor should match the more specific template first, and so foo:bar will be ignored, and all other foo elements matched by the other template.
So, for example, given this input XML
<foo:root xmlns:foo="foo.com">
<foo:bar>No match</foo:bar>
<foo:pie>Match</foo:pie>
</foo:root>
When you apply the following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:foo="foo.com">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="foo:bar" />
<xsl:template match="foo:*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you wanted to do different processing on foo:bar, just add code to the relevant template.
The following is output, without any sign of foo:bar
<foo:root xmlns:foo="foo.com">
<foo:pie>Match</foo:pie>
</foo:root>
XSLT 1.0:
<xsl:template match="foo:*[not(local-name()='bar')]">
<!--do stuff-->
</xsl:template>
XSLT 2.0:
<xsl:template match="foo:*[. except self::foo:bar]">
<!--do stuff-->
</xsl:template>

How does an XSL document look like if the mirrors the input data?

The typicle XSL usage is:
XML1.xml -> *transformed using xsl* -> XML2.xml
How does an XSL document look like, if I want to simply mirror the input data?
ex:
XML1.xml -> *transformed using xsl* -> XML1.xml
How does an XSL document look like, if
I want to simply mirror the input
data?
There are more than one answers to this question, however all of them could be named "Identity Transform":
<xsl:copy-of select="/"/> This is the shortest, simplest, most efficient and most inflexible, non-extensible and unuseful identity transform.
The classical identity rule, which everybody knows (or should know):
_
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is still very short, one-template transformation, which is so much more extensible and useful identity transform, known also as the "identity rule". Using and overriding the identity transform is the most fundamental and powerful XSLT design pattern, allowing to solve common copy and replace/rename/delete/add problems in just a few lines. Maybe 90%+ of all answers in the xslt tag use this form of the identity transform.
.3. The fine-grained control identity rule, which everybody should know (and very few people know):
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
</xsl:stylesheet>
This is similar to the generally known identity rule defined at 2. above, but it provides a finer control over the XSLT processing.
Typically with 2. the <xsl:apply-templates select="#*|node()"> triggers a number of transformations (for all attributes and child nodes), that can be done in any order or even in parallel. There are tasks where we don't want certain types of nodes to be processed after some other nodes, so we have to plumb the leakage of the identity rule with overriding it with empty templates matching the unwanted nodes and adding other templates in a specific mode to process these nodes "when the time comes"...
.3. is more appropriate for tasks where we want more control and really sequential-type processing.
Some tasks that are very difficult to solve with 2. are easy using 3.
It would look like the identity transform:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is one of the most fundamental XSLT transforms. It matches any attribute or other node, copies what it matches, and then applies itself to all attributes and child nodes of the matched node.
This turns out to be quite powerful for other tasks, too. A common requirement is to copy most of a source file unchanged, while handling certain elements in a special way. This can be solved using the identity transform plus one template for the special nodes. It's a generic, flexible (and short) solution.
This matches every element or attribute and recursively applies the template.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="* | #*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

I want to select all texts (including node names) under a node

I currently have a xml file like this:
<aaa>
<b>I am a <i>boy</i></b>.
</aaa>
How can I get the exact string as: <b>I am a <i>boy</i></b>.? Thanks.
You have to tell XSLT that you want to copy elements through as well. That can be done with an additional rule. Note that I use custom select clauses on my apply-templates elements to select attributes as well as all node-type objects. Also note that the rule for aaa takes precedence, and does not copy the aaa element itself to the output.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="aaa">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<aaa>
<b>I am a <i>boy</i></b>.
</aaa>
How can I get the exact string as:
<b>I am a <i>boy</i></b>.?
The easiest/shortest way to do this in your case is to output the result of the following XPath expression:
/*/node()
This means: "Select all nodes that are children of the top element."
Of course, there are some white-space-only text nodes that we don't want selected, but XSLT can take care of this, so the XPath expression is just as simple as shown above.
Now, to get the result with an XSLT transformation, we use the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select="/*/node()"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, the wanted result is produced:
<b>I am a <i>boy</i></b>.
Do note:
The use of the <xsl:copy-of> xslt instruction (not <xsl:value-of>), which copies nodes, not string values.
The use of the <xsl:strip-space elements="*"/> XSLT instruction, directing the XSLT processor to ignore any white-space-only text node in the XML document.