RE: [xsl] About <xsl:param> and <xsl:with-param>

Subject: RE: [xsl] About <xsl:param> and <xsl:with-param>
From: Stuart Brown <sbrown@xxxxxxxxxxx>
Date: Tue, 5 Nov 2002 12:34:13 -0000
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


Current Thread