Optional parameters when calling an XSL template - xslt

Is there way to call an XSL template with optional parameters?
For example:
<xsl:call-template name="test">
<xsl:with-param name="foo" select="'fooValue'" />
<xsl:with-param name="bar" select="'barValue'" />
</xsl:call-template>
And the resulting template definition:
<xsl:template name="foo">
<xsl:param name="foo" select="$foo" />
<xsl:param name="bar" select="$bar" />
<xsl:param name="baz" select="$baz" />
...possibly more params...
</xsl:template>
This code will gives me an error "Expression error: variable 'baz' not found." Is it possible to leave out the "baz" declaration?
Thank you,
Henry

You're using the xsl:param syntax wrong.
Do this instead:
<xsl:template name="foo">
<xsl:param name="foo" />
<xsl:param name="bar" />
<xsl:param name="baz" select="DEFAULT_VALUE" />
...possibly more params...
</xsl:template>
Param takes the value of the parameter passed using the xsl:with-param that matches the name of the xsl:param statement. If none is provided it takes the value of the select attribute full XPath.
More details can be found on W3School's entry on param.

Personally, I prefer doing the following:
<xsl:call-template name="test">
<xsl:with-param name="foo">
<xsl:text>fooValue</xsl:text>
</xsl:with-param>
I like using explicitly with text so that I can use XPath on my XSL to do searches. It has come in handy many times when doing analysis on an XSL I didn't write or didn't remember.

The value in the select part of the param element will be used if you don't pass a parameter.
You are getting an error because the variable or parameter $baz does not exist yet. It would have to be defined at the top level for it to work in your example, which is not what you wanted anyway.
Also if you are passing a literal value to a template then you should pass it like this.
<xsl:call-template name="test">
<xsl:with-param name="foo">fooValue</xsl:with-param>

Please do not use <xsl:param .../> if you do not need it to increase readability.
This works great:
<xsl:template name="inner">
<xsl:value-of select="$message" />
</xsl:template>
<xsl:template name="outer">
<xsl:call-template name="inner">
<xsl:with-param name="message" select="'Welcome'" />
</xsl:call-template>
</xsl:template>

Related

Function in XSLT

I am using the following formula at a lot of places inside an xsl file.
<xsl:value-of select="format-number($Value div 1000000, '##.##')" />
Is there anyway I can create a function so I can keep the logic at one place and reuse it as per below example?
Example:
<xsl:value-of select="ConvertToMillionAndFormatNumber($Value)" />
There are no custom functions in XSLT 1.0 (unless your processor happens to support them as an extension), but you can use a named template:
<xsl:template name="ConvertToMillionAndFormatNumber">
<xsl:param name="Value" />
<xsl:value-of select="format-number($Value div 1000000, '##.##')" />
</xsl:template>
and call it as:
<xsl:call-template name="ConvertToMillionAndFormatNumber">
<xsl:with-param name="Value" select="your-value-here"/>
</xsl:call-template>

In XSLT, how can I get the value of a nodeset parameter inside my template?

I have an XSLT template which defines a parameter like this:
<xsl:param name="weight"/>
I call it like this:
<xsl:call-template name="MakeBlock">
<xsl:with-param name="weight" select="normal"/>
[...]
Inside my template, I try to use the $weight parameter like this:
<xsl:attribute name="font-weight">
<xsl:copy-of select="$weight"/>
</xsl:attribute>
I have also tried this:
<fo:block font-weight="$weight"/>
In both cases, it's not rendering to the output.
When I debug in Visual Studio and stop on a breakpoint, then show the value of the $weight parameter, I get this as its value:
{Dimension:[0]}
My understanding of this is that the parameter is a nodeset of some kind. So, I have tried this to "get at" its value:
exsl:node-set($weight)/
I have Googled quite a bit and found a bunch of examples which tell me that should work. And yes, I have handled the namespace like this:
<xsl:stylesheet xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" [other stuff...] />
How do I use this parameter inside my template? (I am using the default XSLT 1.0 transform inside C#, if that matters.)
Edit:
Here is the full template:
<xsl:template name="MakeBlock">
<xsl:param name="font-size"/>
<xsl:param name="font-family"/>
<xsl:param name="weight"/>
<xsl:param name="space-after"/>
<xsl:param name="content"/>
<fo:block font-weight="{$weight}" font-size="{$font-size}pt" font-family="{$font-family}" space-after="{$space-after}pt">
<xsl:value-of select="$content"/>
</fo:block>
</xsl:template>
And here is how it's called:
<xsl:template match="p">
<xsl:call-template name="MakeBlock">
<xsl:with-param name="font-size" select="11"/>
<xsl:with-param name="font-family">PT Sans</xsl:with-param>
<xsl:with-param name="weight" select="normal"/>
<xsl:with-param name="space-after" select="11"/>
<xsl:with-param name="content">
<xsl:value-of select="."/>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Note that the space-after and font-size params work fine, but the font-weight and font-family params both have the problem I note above. I'm guessing it has something to do with how string params are handled vs. numeric params?
If you want to pass a literal string value then you need <xsl:with-param name="weight" select="'normal'"/>.

Usage of dollar "$" in xsl

In the following piece of code, I want to understand what is "$type" here and how it is being used.
How this if condition is being applied using "$type".
<xsl:template name="CodValue">
<xsl:param name="type"/>
<xsl:param name="nodeNM">category</xsl:param>
<xsl:element name="{$nodeNM}">
<xsl:if test="$type">
<xsl:attribute name="xsi:type">
<xsl:value-of select="$type"/>
</xsl:attribute>
</xsl:if>
</xsl:element>
</xsl:template>
For this template, you need a parameter, another one is optional nodeNM. You can call the parameter like this:
<xsl:call-template name="CodValue">
<xsl:with-param name="type" select="123" />
</xsl:call-template>
or
<xsl:call-template name="CodValue">
<xsl:with-param name="type">123</xsl:with-param>
</xsl:call-template>
type is a variable in CodValue, so you can print it via
<xsl:value-of select="$type" />
or via {$type} in attributes.
Suggestions:
$nodeNM looks like the name of a tag (html-tags if you produce HTML-Code).
$type (if returns true from xpath, ie. if not empty) will create a xsi:type-Tag-Attribute.
So if you call
<xsl:call-template name="CodValue">
<xsl:with-param name="type" select="123" />
</xsl:call-template>
Your XML will be transformed into
<category xsi:type="123" />
$ is used to reference a variable inside an XPath expression.
In this particular case, $type is declared earlier by <xsl:param name="type"/>. However, it has not been given a value so you will need to use <xsl:with-param> when calling the template to so that you can provide a value.
n.b. The variable $nodeNM was given a default value, so you don't need to specify that when calling the template.

XSLT Logic Overwrite a temporary variable

How we can achieve below logic.
The UniqueID is the temporary variable.PALLET_NUMBER is coming from input.
if PALLET_NUMBER!=NULL then
UniqueID=substring (PALLET_NUMBER, 10)
if PALLET_NUMBER =NULL then
UniqueID=substring (CARTON_NUMBER, 7)
we can get the value of UniqueID from the above two conditions.These things happen in iteration loop.How we can overwrite the UniqueID temporary variable.
bacause later There is a condition we need to put like
<foreach>
If previous UniqueID != current UniqueID then
<Some code>
<IF>
</foreach>
You cannot overwrite variable in xslt.
You may want to look up xslt extension functions to achieve your goal.
As Treemonkey, says, it's not possible to overwrite variables, but you can achieve something like you describe using recursion:
Suppose you had something like this:
<xsl:for-each select="my/node/isNamed/something" />
You could do this instead:
<xsl:variable name="items" select="my/node/isNamed/something" />
<xsl:apply-templates select="$items[1]">
<xsl:with-param name="remainder" select="$items[position() > 1" />
</xsl:apply-templates>
<!-- Separate template -->
<xsl:template match="something">
<xsl:param name="remainder" />
<xsl:param name="lastId" />
<xsl:variable name="uniqueId" select="..." />
<!-- Contents of the xsl:for-each -->
<xsl:apply-templates select="$remainder[1]">
<xsl:with-param name="remainder" select="$remainder[position() > 1]" />
<xsl:with-param name="lastId" select="$uniqueId" />
</xsl:apply-templates>
</xsl:template>

XSLT "contains" function to check for period

Ok, I'm stumped. I would like test if a parameter sent to an XSLT template contains a period and to print out quotes if it does not. The parameter I would like to test is "value" in the template below. It seems the contains function should work, but for some reason the quotes always get outputted regardless the contents of "value". What am I doing wrong? Thanks
<!-- Add a JSON property -->
<xsl:template name="addProperty">
<xsl:param name="name" />
<xsl:param name="value" />
<xsl:value-of select="$name" />
<xsl:text>:</xsl:text>
<xsl:if test="not(contains($value,'.'))">'</xsl:if>
<xsl:value-of select="$value" />
<xsl:if test="not(contains($value,'.'))">'</xsl:if>
<xsl:text>,</xsl:text>
</xsl:template>
When I call your template, it works fine. How are you calling it? This is what I used:
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'abc'"/>
<xsl:with-param name="value" select="'123'"/><!-- quoted number -->
</xsl:call-template>
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'abc'"/>
<xsl:with-param name="value" select="123"/><!-- NOT quoted number -->
</xsl:call-template>
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'xyz'"/>
<xsl:with-param name="value" select="'456.789'"/><!-- quoted number -->
</xsl:call-template>
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'xyz'"/>
<xsl:with-param name="value" select="456.789"/><!-- NOT quoted number -->
</xsl:call-template>
And this is what I got as output:
abc:'123',abc:'123',xyz:456.789,xyz:456.789,
Could you not be passing in the values to the named template that you think you are passing in? What XSLT engine are you using?
A good way to test this is to add something like this to your named template and see what it produces, if you don't have a good debugger handy:
XXX<xsl:value-of select="$value"/>XXX
YYY<xsl:value-of select="contains($value, '.')"/>YYY
ZZZ<xsl:value-of select="not(contains($value, '.'))"/>ZZZ