Discussion:
About <xsl:param> and <xsl:with-param>
Hélder Sousa
2002-11-05 11:54:41 UTC
Permalink
Hi..

In the next xsl I want to make that the param "cod" be incremented in each time that the <xsl:for-each> cicle occurs, but it's result is an error:

"javax.xml.transform.TransformerException: xsl:with-param is not allowed in this position in the stylesheet!"

Why?!
Tks

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:xalan="http://xml.apache.org/xslt">
<xsl:param name="cod">0</xsl:param>
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select=".//Boy">
<xsl:with-param name="cod" select="number($cod+1)"/>
<!--make other things-->
</xsl:for-each>
</xsl:template>
</xsl:transform>


XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Stuart Brown
2002-11-05 12:34:13 UTC
Permalink
Hi Hélder

<snip>
In the next xsl I want to make that the param "cod" be incremented in each
time that the <xsl:for-each> cicle occurs, but it's result is an error:

"javax.xml.transform.TransformerException: xsl:with-param is not allowed in
this position in the stylesheet!"

Why?!
Tks

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:xalan="http://xml.apache.org/xslt">
<xsl:param name="cod">0</xsl:param>
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select=".//Boy">
<xsl:with-param name="cod" select="number($cod+1)"/>
<!--make other things-->
</xsl:for-each>
</xsl:template>
</xsl:transform>
</snip>

There's a few errors here. Firstly, there's the issue of what you're
actually trying to do. XSL is a functional not a procedural language and
therefore once parameters are set (either globally or locally) they cannot
be altered. These, and variables (known as the variable-binding elements),
are variables in the mathematical sense that they represent any value, not
in the literal sense that they "vary"! You should not think in terms of a
for-each being an iteration. Each matching node is processed independantly
of the others.

Secondly, parameters (and variables) can only be defined at the top level of
the stylesheet (as a child of <xsl:stylesheet>) or as a child of
<xsl:template>. That is, globally to the whole process or locally to the
context of a specific template.

The <xsl:with-param> exists not to reassign an existing parameter, but to
pass a parameter onto another template (overriding any definition of that
parameter within the template) and occurs as a child of <xsl:call-template>.

All of this probably doesn't help you that much, but it leads towards what
you want to acheive. Instead of viewing the <xsl:for-each> as an iteration
with an incrementing parameter, see it as calling another template. If you
then pass this template a parameter which is defined in terms of the number
of preceding Boy elements, you effectively have the increment you are
looking for.

Thus:

<!-- Do not define the parameter globally -->
<xsl:template match="/">
<xsl:for-each select=".//Boy">
<!-- Call the named (not matched) template to handle Boy -->
<xsl:call-template name="doBoy">
<!-- Define the parameter in terms of the number of preceding Boys -->
<xsl:with-param name="cod" select="count(preceding::Boy)+1"/>
<xsl:call-template/>
</xsl:for-each>
</xsl:template>

<!-- Handle Boy -->
<xsl:template name="doBoy">
<!-- Blank parameter which will received the passed value -->
<xsl:param name="cod"/>
<xsl:text>This is Boy number </xsl:text>
<xsl:value-of select="$cod"/>
</xsl:template>

Or in this instance you could also simply define the parameter directly
within doBoy:

<xsl:template match="/">
<xsl:for-each select=".//Boy">
<!-- Call the named (not matched) template to handle Boy -->
<xsl:call-template name="doBoy"/>
</xsl:for-each>
</xsl:template>

<!-- Handle Boy -->
<xsl:template name="doBoy">
<!-- Parameter defined here -->
<xsl:param name="cod" select="count(preceding::Boy)+1"/>
<xsl:text>This is Boy number </xsl:text>
<xsl:value-of select="$cod"/>
</xsl:template>

I think you probably need to read up a bit on XSLTs processing model and the
handling of variable-binding elements. See
http://www.dpawson.co.uk/xsl/sect2/N8090.html and
http://www.dpawson.co.uk/xsl/sect2/N5982.html for starters.

Cheers,

Stuart






XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
David Carlisle
2002-11-05 13:18:57 UTC
Permalink
"javax.xml.transform.TransformerException: xsl:with-param is not allowed in
this position in the stylesheet!"

Why?!

xsl:param has to be the first eleemnt of xsl:stylesheet or xsl:template
It specifies a parameter to the template. xsl:for-each does not take a
parameter.


parameters and variables in XSLT, as for all declarative programming
languages, do not change their value once bound, so within a given
template any occurrence of your parameter will always have th esame
value, although of course you can call the template multiple times with
different values for the parameter each time.

David

_____________________________________________________________________
This message has been checked for all known viruses by Star Internet
delivered through the MessageLabs Virus Scanning Service. For further
information visit http://www.star.net.uk/stats.asp or alternatively call
Star Internet for details on the Virus Scanning Service.

XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
J***@nokia.com
2002-11-05 12:45:59 UTC
Permalink
FAQ,
Post by Hélder Sousa
In the next xsl I want to make that the param "cod" be
incremented in each time that the <xsl:for-each> cicle
"javax.xml.transform.TransformerException: xsl:with-param is
not allowed in this position in the stylesheet!"
Why?!
Tks
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:xalan="http://xml.apache.org/xslt">
<xsl:param name="cod">0</xsl:param>
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select=".//Boy">
<xsl:with-param name="cod"
select="number($cod+1)"/>
<!--make other things-->
</xsl:for-each>
</xsl:template>
</xsl:transform>
XSLT doesn't allow you to update parameter values after they've been bound. Repeat "recursion is my friend" ten times, and you'll get the hang of it.

Anyhow, rewrite your stylesheet to

<xsl:param name="cod" select="0" />
<xsl:template match="/">
<xsl:for-each select="//Boy">
<xsl:variable name="_cod" select="$cod + position()" />
<!--make other things-->
</xsl:for-each>
</xsl:template>

And use $_cod instead of $cod in "make other things". Use select attribute in the xsl:param to bind the parameter into a number, instead of a RTF <http://www.w3.org/TR/xslt#section-Result-Tree-Fragments>.

Cheers,

Jarno

XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Loading...