Why doesn't it see the variable inside element declaration? - xslt

The following code gives an error:
[1.0-ml] XDMP-UNDVAR: (err:XPST0008) Undefined variable $ename
However if I change the xsl:element to <xsl:element name="yyyXXX"> it shows that it sees $ename in value-of, i.e.
<yyyXXX>zzz</yyyXXX>
The stylesheet below works just fine in Saxonica 9.x
Thanks.
xquery version "1.0-ml";
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xdmp="http://marklogic.com/xdmp"
extension-element-prefixes="xdmp"
version="2.0">
<xsl:template match="/">
<xsl:variable name="ename" select="'zzz'"/>
<xsl:element name="yyy{$ename}">
<xsl:value-of select="$ename"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
,document{ <doc/> })

You need two sets of curly brackets around your element name, e.g. yyy{{$ename}}. That tells the outer XQuery to treat those as literal curly brackets in the XSLT. If you were invoking the XSLT from an external document (e.g. with xdmp:xslt-invoke) you wouldn't need the extra brackets; this is equivalent to your Saxon test. The ability to evaluate XQuery to dynamically create XSLT is pretty powerful, but does impose a little more cognitive overhead.

Related

How do I know how many tags an xsl:variable contains?

In XSLT 1.0, if I have an <xsl:variable> declared like that:
<xsl:variable name="ListeEcheances">
<bla/><bli/>
</xsl:variable>
How do I know if it's empty? Or even better: how do I know how many tags it contains? (I know there are 2 tags here, but my real code is a little bit more complex :))
<xsl:when test="$ListeEcheances=''"> returns true (it doesn't count the tags, only the text) ;
<xsl:when test="count($ListeEcheances/*) > 0"> sadly doesn't compile.
Thank you for your help.
This is indeed incorrect and your compiler is correct in throwing an error. You can only count a node set, you cannot count a result tree fragment. What you need is transform the variable in a node-set by using an extension function.
For Saxon 6.5 this would be exsl:node-set.This works with Saxon 6.5 and any processor that supports the EXSLT node-set function (most do). EDIT: Jirka Kosek wrote down a list of node-set extensions per processor, I'm sure yours is in the list.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:exsl="http://exslt.org/common">
<xsl:variable name="ListeEcheances">
<bla/><bli/>
</xsl:variable>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="count(exsl:node-set($ListeEcheances)/*) > 0">
<xsl:text>Larger then zero!</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>Larger then zero!
Note: if you were to use XSLT 2.0, everything is a node-set and you don't run into this awkwardness of XSLT 1.0, where result tree fragments are next to useless.
If the content of the variable is declared in the XSLT as shown in your example, rather than dynamically evalueated, you can use the document() function to parse the XSLT file(which is an XML file) and evaluate an XPath expression to count the elements in the variable:
count(document('')/*/xsl:variable[#name='ListeEcheances']/*)
Using the document function with an empty path will load the base URI of the current stylesheet.
Try <xsl:when test="count($ListeEcheances/*) > 0">
or wait - maybe you get something like
Expression must evaluate to a node-set.
count(-->$ListeEcheances<--/*) > 0
The reason is that the variable is a result tree fragment, not a node-set.
In XSLT 1.0 you will need to apply the node-set function, available in a namespace dependent on the processor.
For instance: <xsl:when test="count(msxsl:node-set($ListeEcheances/*)) > 0">
If that does not work, or if you can't discover the namespace to use, then a trick might help:
<xsl:variable name="temp" select="$ListeEcheances"/>
<xsl:when test="count($temp/*) > 0">
The reason that this works can be found in stackoverflow rtf to node-set

XSL namespaces and nested xsl:for-each

I have below XML and would like to iterate through the element as such the i could display it in some format like:
PIN 1<br/>
XYZ<br/>
HELLO<br/>
PIN 2<br/>
ABC<br/>
HI<br/>
XML:
<RootResponse xmlns:ip="urn:domain:tx:inPayment" xmlns:ipn="urn:domain:tx:Pin">
<OutBoundMessage>
<ip:InfoMessage>
<ipn:Alert>PIN 1</ipn:Alert>
<ipn:Code>
<ip:CodeLabel>XYZ</ip:CodeLabel>
<ip:CodeMessage>HELLO</ip:CodeMessage>
</ipn:Code>
</ip:InfoMessage>
<ip:InfoMessage>
<ipn:Code>
<ipn:Alert>PIN 2</ipn:Alert>
<ip:CodeLabel>ABC</ip:CodeLabel>
<ip:CodeMessage>HI</ip:CodeMessage>
</ipn:Code>
</ip:InfoMessage>
</OutBoundMessage>
</RootResponse>
I Can't seem to find a solution. Any suggestion?
I would recommend following the W3C schools XSLT tutorial, this should give you all you need to solve this relatively simple XSLT problem.
You are right that you will have to pay attention to namespaces, although again this is quite straightforward. Simply ensure that your XSLT defines the namespaces required, and that you prefix element names in your XPath statements accordingly. See the following:
XML Namespaces and How They Affect XPath and XSLT
You should declare the namespaces in your XSLT and then use the declared prefix in your expressions.
Below is an example of how to do that, using templates(i.e. "push style") rather than xsl:for-each (e.g. "pull style").
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ip="urn:domain:tx:inPayment"
xmlns:ipn="urn:domain:tx:Pin"
exclude-result-prefixes="ip ipn">
<xsl:output indent="yes" />
<xsl:template match="ipn:Alert">
<xsl:text>
</xsl:text>
<xsl:apply-templates />
<br/>
</xsl:template>
<xsl:template match="ip:*[starts-with(local-name(),'Code')]">
<xsl:text>
  </xsl:text>
<xsl:apply-templates/>
<br/>
</xsl:template>
</xsl:stylesheet>

Create node set and pass as a parameter

Using XSLT 1.0, I'm trying to essentially create a small node set and then pass it as a parameter to a template, something like the following:
<xsl:call-template name="widget">
<xsl:with-param name="flags">
<items>
<item>widget.recent-posts.trim-length=100</item>
<item>widget.recent-posts.how-many=3</item>
<item>widget.recent-posts.show-excerpt</item>
</items>
</xsl:with-param>
</xsl:call-template>
The idea is that then from within the widget template I could write something like:
<xsl:value-of select="$flags/item[1]" />
Obviously I get compile errors.. how can I achieve this sort of thing?
There is a way (non-standard) in XSLT 1.0 to create temporary trees dynamically and evaluate XPath expressions on them, however this requires using the xxx:node-set() function.
Whenever nodes are dynamically created inside the body of an xsl:variable or an xsl:param, the type of that xsl:variable / xsl:param is RTF (Result Tree Fragment) and the W3 XSLT 1.0 Spec. limits severyly the kind of XPath expressions that can be evaluated against an RTF.
As a workaround, almost every XSLT 1.0 vendor has their own xxx:node-set() extension function that takes an RTF and produces a normal node-set from it.
The namespace to which the xxx prefix (or any other prefix you choose) is bound is different for different vendors. For MSXML and the two .NET XSLT processor it is: "urn:schemas-microsoft-com:xslt". The EXSLT library uses the namespace: "http://exslt.org/common". This namespace EXSLT is implemented on many XSLT 1.0 processors and it is recommended to use its xxx:node-set() extension, if possible.
Here is a quick example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext msxsl"
>
<xsl:template match="/">
<xsl:variable name="vTempRTF">
<a>
<b/>
</a>
</xsl:variable>
<xsl:copy-of select="ext:node-set($vTempRTF)/a/*"/>
</xsl:template>
</xsl:stylesheet>
Well, I managed to get around this in the following way:
First add a custom namespace to your stylesheet, e.g. xmlns:myns="http://my.ns.com"
Then define the nodeset at the top of the stylesheet:
<myns:recent-posts-flags>
<item>widget.recent-posts.trim-length=100</item>
<item>widget.recent-posts.how-many=3</item>
<item>widget.recent-posts.show-excerpt</item>
</myns:recent-posts-flags>
Then reference in the following way:
<xsl:call-template name="widget">
<xsl:with-param name="flags" select="document('')/*/myns:recent-posts-flags" />
</xsl:call-template>
This works, but it would still be ideal for me to define the node-set within the <xsl:with-param> tag itself, as in the first example I gave.. anyone think that would be possible?

Why does XSLT output all text by default?

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.

Parameters not being passed into template when using the .Net transform classes

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.