When there is no matching template, why is are the text (characters) output?
XML:
<?xml version="1.0" encoding="utf-8" ?>
<base>
<a>
<b>B in a</b>
</a>
<c>
<b>B in c</b>
</c>
</base>
XLST:
<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
<output method="text"/>
<template match="/a">Found it!</template>
</stylesheet>
Output is: with newlines removed:
B in a
B in c
This is due to the built-in template rules, which will by default recursively process all of the elements and return all of the text().
There is a built-in template rule to allow recursive processing to continue in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
There is also a built-in template rule for each mode, which allows recursive processing to continue in the same mode in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule for mode m.
<xsl:template match="*|/" mode="m">
<xsl:apply-templates mode="m"/>
</xsl:template>
There is also a built-in template rule for text and attribute nodes that copies text through:
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
The built-in template rule for processing instructions and comments is to do nothing.
<xsl:template match="processing-instruction()|comment()"/>
The built-in template rule for namespace nodes is also to do nothing. There is no pattern that can match a namespace node; so, the built-in template rule is the only template rule that is applied for namespace nodes.
The built-in template rules are treated as if they were imported implicitly before the stylesheet and so have lower import precedence than all other template rules. Thus, the author can override a built-in template rule by including an explicit template rule.
Related
I have the following XML
<?xml version="1.0"?>
<people><human><sex>male</sex><naxme>Juanito</naxme>
</human>
<human><sex>female</sex><naxme>Petra</naxme></human>
<human><sex>male</sex><naxme>Anaximandro</naxme></human>
</people>
and this XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="no"/>
<xsl:template match="/people/human[sex='male']">
<xsl:value-of select="naxme"/>
</xsl:template>
</xsl:stylesheet>
I'm expecting it to filter out the female, and it kind of works but I get odd values for the non-matching nodes:
Juanito
femalePetra
Anaximandro
I'm expecting the same output as with
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="no"/>
<xsl:template match="/people/human">
<xsl:if test = "sex='male'">
<xsl:value-of select="naxme"/>
</xsl:if> </xsl:template>
</xsl:stylesheet>
Thanks!
I'll expand on Daniels answer which covers a little of the why.
The reason you are getting two different outputs comes down to the built-in template rules, and how nodes and text are treated by default.
Summarising that link, if no other template exists, there are default templates that ensure every node - be it element, text, attribute, comment - will be encoutered, just to ensure that other nodes that do have rules can be processed correctly.
With this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/people/human[sex='male']">
<xsl:value-of select="naxme"/>
</xsl:template>
</xsl:stylesheet>
you have an explicit rule that says:
If you find a node that matches the XPath /people/human[sex='male] do this template.
Along with the default rule:
Find all nodes, and then process all of their children. If it is text, just output the text.
This default rule is why your template is being processed, since you have no explicit rule for the root node - / - it any every child and grandchild node are processed using the default rules, unless another exists. As such, each node is traversed using the defaults, except for the nodes matching /people/human[sex='male]. The result of this is that when you have a node that is "female" the text is being spat out instead of ignored.
However, contrast this with:
<xsl:template match="/people/human">
<xsl:if test = "sex='male'">
<xsl:value-of select="naxme"/>
</xsl:if>
</xsl:template>
Where the rule becomes:
If you find a node that matches the XPath /people/human do this template.
It just so happens that in that template, you have an extra condition that says, if it is male, then process it in some way, with no other conditions, so if a "female" node is encountered it is now blank in the output.
Lastly, the reason why Daniels answer works but could easily break, is that it changes the rule for processing text. Instead of now copying all text as in the default rules, it outputs nothing (as per the empty template. However, if you had any other templates which used xsl:apply-templates to process text and were expecting text, they would also now output nothing.
It's probably because of XSLT's built-in template rules. Try adding this template:
<xsl:template match="text()"/>
I have 2 Templates
<template match="vehicle_details[preceding-sibling::vehicle_type = '4x4']/*">
...
</xsl:template>
<xsl:template match="vehicle_details[descendant::color = 'red']/*" >
...
</xsl:template>
My question is: which template will take precedence on transformation. And can someone give me an overview/resources about XSL template precedence?
Thanks in advance!
The full resolution process is described in section 5.5 of the XSLT spec.
In general, the following rules apply in order (e.g. a template eliminated from consideration due to lower import precedence is eliminated permanently, regardless of its priority):
Imported templates have lower precedence than templates in the primary stylesheet
Templates with a higher value in their priority attribute have higher precedence
Templates without a priority attribute are assigned a default priority. Templates with more specific patterns take precedence.
It's an error if the previous three steps leave more than one template in consideration, but XSLT processors can recover by defaulting to the last one in the file.
In your specific case both templates have the same priority, so #4 above applies. To demonstrate:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match=
"vehicle_details[preceding-sibling::vehicle_type = '4x4']/*">
template1
</xsl:template>
<xsl:template match="vehicle_details[descendant::color = 'red']/*">
template2
</xsl:template>
</xsl:stylesheet>
Applied to this input (both templates match):
<root>
<vehicle_type>4x4</vehicle_type>
<vehicle_details>
<color>red</color>
</vehicle_details>
</root>
Output:
template2
But if we swap the order of the templates:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="vehicle_details[descendant::color = 'red']/*">
template2
</xsl:template>
<xsl:template match=
"vehicle_details[preceding-sibling::vehicle_type = '4x4']/*">
template1
</xsl:template>
</xsl:stylesheet>
Then the output is:
template1
Here's a simple case.
Here's my XML:
<?xml version="1.0" encoding="utf-8" ?>
<dogs>
<dog type="Labrador">
<Name>Doggy</Name>
</dog>
<dog type="Batard">
<Name>Unknown</Name>
</dog>
</dogs>
This XML is used with two Xslt. This is the common one:
<?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="text"/>
<xsl:template match="dogs">
<xsl:text>First template
</xsl:text>
<xsl:apply-templates select="." mode="othertemplate" />
</xsl:template>
</xsl:stylesheet>
This is the child one:
<?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:include href="transform.xslt"/>
<xsl:template match="dogs" mode="othertemplate">
<xsl:text> Other template</xsl:text>
</xsl:template>
</xsl:stylesheet>
The child includes the common one (called transform.xslt).
When I execute the child, I get the expected result:
First template
Other template
When I execute the common one, I get this strange results:
First template
Doggy
Unknown
The common one applies a template with the mode "othertemplate". This mode is only included, some times, in the child xslt.
I want that, if there's no template with mode "othertemplate", then nothing should be outputted.
I don't want to include a template with mode "othertemplate" with empty body for all xslt files that does not have to use this template mode...
What should I do?
Thanks
Built-in Template Rules in XSLT
The element contents and the extra whitespace appear because of XSLT's built-in template rules, also known as default templates. These rules are applied when there is no other matching template. The built-in template rules are
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="processing-instruction()|comment()"/>
Built-in template rules process root and element nodes recursively and copy text (and attribute values if attribute nodes are selected). The built-in template rule for processing instructions and comments is to do nothing. Namespace nodes are not processed by default. Note that <xsl:apply-templates/> is practically a shorthand for <xsl:apply-templates select="child::node()"/> so it will not select attribute or namespace nodes.
There is also a built-in template rule for every mode. These templates are like default templates for elements and root except they continue processing in the same mode.
<xsl:template match="*|/" mode="foobar">
<xsl:apply-templates mode="foobar"/>
</xsl:template>
Because your stylesheet doesn't have a template matching dogs with mode othertemplate this built-in template rule is applied which in this case results in processing all child nodes and eventually printing the text nodes. Indentation and line feeds between the source document's elements are also text nodes so they also get printed and cause the extra whitespace in the output.
Warning on non-terminating loops because of <xsl:apply-templates select="."/>
Typically apply-templates is used to process descendants. In your example code you selected the current node when calling apply-templates. This may result in non-terminating loop if the template itself is applied because of an apply-templates command inside it. Example below
<xsl:template match="foobar">
<!-- This is an infinite loop -->
<xsl:apply-templates select="."/>
</xsl:template>
By the way. On a general rule on combining stylesheets, think carefully which template you should run and which one you should import or include. (I have read that as a general practice, Michael Kay seems to recommend using <xsl:import> to import the general-case stylesheet into the special-case stylesheet, not the other way round.)
The built-in XSLT templates are defined and selected for every mode. So, the built-in template for text nodes is selected and (by definition) it outputs the text node.
To suppress this, you need to override thie built-in template for text nodes (also possibly for elements) in your desired mode with an empty template:
<xsl:template match="text()" mode="othertemplate"/>
Include the above in your imported stylesheet.
Hi I had performed a transformation which drops a tag if it is null.
I wanted to check whether my transformation is working fine, so instead of checking it manually, I wrote one more XSLT code which just checks the presence of that particular tag in the OUTPUT XML, if it is null, then the second XSLT should output a text "FOUND". (I don't actually need some XML kind of output but I am just using XSLT for searching.)
When I tried with this XSL code ::
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/SiebelMessage//SuppressCalendar[.!='']">
FOUND
</xsl:template>
</xsl:stylesheet>
It outputs all the TEXT DATA that is present in the XML file,
to avoid that, I had to write this code::
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/SiebelMessage//SuppressCalendar[.!='']">
FOUND
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
why did the former code outputs TEXT, why should I insist XSL to ignore all other text? is that the behavior of all XML parsers or only of my own (I am using msxml parser).
why did the former code outputs TEXT,
why should I insist XSL to ignore all
other text? is that the behavior of
all XML parsers or only of my own
You are discovering one of the most fundamental XSLT features as specified in the Specification: the built-in templates of XSLT.
From the Spec:
There is a built-in template rule to
allow recursive processing to continue
in the absence of a successful pattern
match by an explicit template rule in
the stylesheet. This template rule
applies to both element nodes and the
root node. The following shows the
equivalent of the built-in template
rule:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
There is also a built-in template rule
for each mode, which allows recursive
processing to continue in the same
mode in the absence of a successful
pattern match by an explicit template
rule in the stylesheet. This template
rule applies to both element nodes and
the root node. The following shows the
equivalent of the built-in template
rule for mode m.
<xsl:template match="*|/" mode="m">
<xsl:apply-templates mode="m"/>
</xsl:template>
There is also a built-in template rule
for text and attribute nodes that
copies text through:
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
The built-in template rule for
processing instructions and comments
is to do nothing.
<xsl:template match="processing-instruction()|comment()"/>
The built-in template rule for
namespace nodes is also to do nothing.
There is no pattern that can match a
namespace node; so, the built-in
template rule is the only template
rule that is applied for namespace
nodes.
The built-in template rules are
treated as if they were imported
implicitly before the stylesheet and
so have lower import precedence than
all other template rules. Thus, the
author can override a built-in
template rule by including an explicit
template rule.
So, the reported behavior is the result of the application of the built-in templates -- the 1st and 2nd of all three of them.
It is a good XSLT design pattern to override the built-in templates with your own that will issue an error message whenever called so that the programmer immediately knows his transformation is "leaking":
For example, if there is this XML document:
<a>
<b>
<c>Don't want to see this</c>
</b>
</a>
and it is processed with this transformation:
<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="a|b">
<xsl:copy>
<xsl:attribute name="name">
<xsl:value-of select="name()"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
the result is:
<a name="a">
<b name="b">Don't want to see this</b>
</a>
and the programmer will be greatly confused how the unwanted text appeared.
However, just adding this catch-all template helps avoid any such confusion and catch errors immediately:
<xsl:template match="*">
<xsl:message terminate="no">
WARNING: Unmatched element: <xsl:value-of select="name()"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
Now, besides the confusing output the programmer gets a warning that explains the problem immediately:
WARNING: Unmatched element: c
Later Addition by Michael Kay for XSLT 3.0
In XSLT 3.0, rather than adding a catch-all template rule, you can specify the fallback behaviour on an xsl:mode declaration. For example, <xsl:mode on-no-match="shallow-skip"/> causes all nodes that are not matched (including text nodes) to be skipped, while <xsl:mode on-no-match="fail"/> treats a no-match as an error, and <xsl:mode warning-on-no-match="true"/> results in a warning.
There are several built in template rules in XSL, one of which is this:
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
It outputs text.
I am using the .Net XslCompiledTranform to run some simple XSLT (see below for a simplified example).
The example XSLT is meant to do simply show the value of the parameter that is passed in to the template. The output is what I expect it to be (i.e.
<result xmlns:p1="http://www.doesnotexist.com">
<valueOfParamA>valueA</valueOfParamA>
</result>
when I use Saxon 9.0, but when I use XslCompiledTransform (XslTransform) in .net I get
<result xmlns:p1="http://www.doesnotexist.com">
<valueOfParamA></valueOfParamA>
</result>
The problem is that that the parameter value of paramA is not being passed into the template when I use the .Net classes. I completely stumped as to why. when I step through in Visual Studio, the debugger says that the template will be called with paramA='valueA' but when execution switches to the template the value of paramA is blank.
Can anyone explain why this is happening? Is this a bug in the MS implementation or (more likely) am I doing something that is forbidden in XSLT?
Any help greatly appreciated.
This is the XSLT that I am using
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:extfn="http://exslt.org/common" exclude-result-prefixes="extfn" xmlns:p1="http://www.doesnotexist.com">
<!--
Replace msxml with
xmlns:extfn="http://exslt.org/common"
xmlns:extfn="urn:schemas-microsoft-com:xslt"
-->
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="resultTreeFragment">
<p1:foo>
</p1:foo>
</xsl:variable>
<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>
<result>
<xsl:apply-templates select="$nodeset" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="p1:foo" mode="AParticularMode">
<xsl:param name="paramA"/>
<valueOfParamA>
<xsl:value-of select="$paramA"/>
</valueOfParamA>
</xsl:template>
</xsl:stylesheet>
There is nothing strange -- this is the expected behavior of any XSLT 1.0 -compliant processor.
Explanation:
The $nodeset variable defined as:
<xsl:variable name="nodeset" select="extfn:node-set($resultTreeFragment)"/>
in XSLT 1.0 contains a complete xml document -- a document node, denoted in XPath 1.0 by / .
Therefore,
<xsl:apply-templates select="$nodeset" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
Will aplly a template matching the tree (the document node /) in the specified mode, if such template exists. In your case no such template exists. Therefore the built-in XSLT 1.0 template for / is applied (which belongs to every mode).
The text of the built-in template can be found in the spec:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
As per spec:
"There is also a built-in template rule for each mode, which allows recursive processing to continue in the same mode in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule for mode m.
<xsl:template match="*|/" mode="m">
<xsl:apply-templates mode="m"/>
</xsl:template>
"
Of course, the built-in template doesn't know anything about your parameter $paramA and it doesn't pass it down to the applied templates.
Thus, finally, your template matching p1:foo" in mode="AParticularMode" is selected for processing. Nothing is passed as value for the parameter, so it has no value -- thus the <xsl:value-of> doesn't produce even a single character or node.
To correct this problem, simply add a template matching / and in mode "AParticularMode":
<xsl:template match="/" mode="AParticularMode">
<xsl:param name="paramA"/>
<xsl:apply-templates mode="AParticularMode">
<xsl:with-param name="paramA" select="$paramA"/>
</xsl:apply-templates>
</xsl:template>
and now you get the desired result.
In XSLT 2.0 (Saxon 9) you observe different behavior, because the built-in templates in XSLT 2.0 by definition retransmit all parameters with which they were applied -- see the XSLT 2.0 Spec :
"If the built-in rule was invoked with parameters, those parameters
are passed on in the implicit xsl:apply-templates instruction."
Answer for your question,
Why my first attempt failed?
As you are using node-set() comfortably in your code I guess you might be well aware of Result Tree Fragment. If not, then go through this link.
Well. By taking advantage of RTF[Result tree fragment] you are able to treat "foo" as a node.
The variable $nodeset has stored the tree-structure of the node, so that you can treat it's value as node-set, where as variable $nodeset is still a variable. If you want to apply-template then apply on, it's child nodes[precisely elements] appearing as it's value,
Instead of * you could have used,
<xsl:apply-templates select="$nodeset/p1:foo" mode="AParticularMode">
This is more precise,
Well after more experimenting I found that altering the apply-templates to
<xsl:apply-templates select="$nodeset/*" mode="AParticularMode">
<xsl:with-param name="paramA" select="'valueA'"/>
</xsl:apply-templates>
(note the select="$nodeset/*" instead of select="nodeset") made it work as I wanted it to in .Net and Saxon.
I would however still be grateful if someone can explain why my first attempt failed.