Why does XSLT output all text by default?
XsltXslt 1.0Xslt 2.0Xslt Problem Overview
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).
Xslt Solutions
Solution 1 - Xslt
> 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:
>
>
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.
Solution 2 - Xslt
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.