Usage of dollar "$" in xsl - xslt

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.

Related

Sum value of output of template in Apache FOP

I am using Apache FOP to generate a PDF document, and to display a certain value I have to iterate over a number of nodes to determine a total price value, then sum that value. So far I have a function that iterates over an array and then retrieves the intended value, but the issue occurs when I try to sum the results.
<xsl:function name="foo:buildTotalValue">
<xsl:param name="items" />
<xsl:variable name="totals">
<xsl:for-each select="$items/charge">
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="sum(exsl:node-set($totals))" />
</xsl:function>
<xsl:template name="getTotalPriceNode">
<xsl:param name="itemParam" />
<xsl:choose>
<xsl:when test="$itemParam/Recurrance = 'OnceOff'">
<xsl:value-of select="$itemParam/TotalValue" />
</xsl:when>
<xsl:when test="$itemParam/Recurrance = 'Monthly'">
<xsl:value-of select="$itemParam/TotalValue * $itemParam/Months"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
I'm hoping that when I pass in foo:buildTotalValue with entries like this:
<Charges>
<Charge>
<Recurrance>OnceOff</Recurrance>
<TotalValue>50.00</TotalValue>
</Charge>
<Charge>
<Recurrance>Monthly</Recurrance>
<TotalValue>10.00</TotalValue>
<Months>6</Months>
</Charge>
</Charges>
would return with the value 110.00, but instead I get the error:
Cannot convert string "50.0060.00" to double
I've tried adding a <value> or something in the templates and then using that as a selector for the exsl:node-set function but it doesn't seem to make a difference.
AFAICT, the problem with your function is that it builds a concatenated string of values returned by the called template, instead of a tree of nodes that can be converted into a node-set and summed.
Try changing:
<xsl:for-each select="$items/charge">
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</xsl:for-each>
to:
<xsl:for-each select="$items/charge">
<total>
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</total>
</xsl:for-each>
and:
<xsl:value-of select="sum(exsl:node-set($totals))" />
to:
<xsl:value-of select="sum(exsl:node-set($totals)/total)" />
Untested, because (see comment to your question).
I ended up using the suggestion from Martin from the comment - the xpath 2+ expression along the line of:
sum(Charge[Recurrance = 'OnceOff']/TotalValue | Charge[Recurrance = 'Monthly']/(TotalValue * Months))
which was able to achieve what I needed without the use of functions / templates / node-set (And in a lot less code)

xsl:param used without being assigned a value

I am looking at this xslt template:
<xsl:template match="row">
<xsl:param name="spans"/>
<xsl:param name="browserows"/>
<xsl:choose>
<xsl:when test="contains($spans, '0')">
<xsl:call-template name="normal-row">
<xsl:with-param name="spans" select="$spans"/>
<xsl:with-param name="browserows" select="$browserows"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
I am trying to understand where the value of $spans is coming from at the test statement on line 6, but it looks like the value was never assigned.
I cannot find spans as a global param anywhere.
Am I missing something?
It would come from the calling code that executed the <xsl:apply-templates> that matched against that row - because the <xsl:template> declares that parameter in <xsl:param name="spans"/>. If you didn't specify a value via <xsl:with-param>, then it is presumably nil.

How to make template call work in xslt 1.0

I have this following template which i am not sure correct or not
<xsl:template match="text()" name="createName">
<xsl:param name="Type"/>
<xsl:if test="contains(Type,'NEW')">
<xsl:value-of select="concat('New','Goal')"/>
</xsl:if>
<xsl:if test="contains(Type,'AMD')">
<xsl:value-of select="concat('Amended','Goal')"/>
</xsl:if>
</xsl:template>
Calling this template here
<xsl:element name="PackageName">
<xsl:call-template name="createName">
<xsl:with-param name="Type" select="s0:PIXField/s0:TransactionID"/>
</xsl:call-template>
</xsl:element>
s0:PIXField/s0:TransactionID = BPA201605311506452806320060A1AMD
I want to create PackageName element with value 'Amended Goal'.
But for now i am getting empty PackageName. dont know where my code fails.
Please help.
If you want to access a parameter, you need to use the $ prefix. That is to say, do contains($Type,'NEW') and not contains(Type,'NEW'). The latter is looking for a child element called Type, and not the passed in parameter
Try this template. Note that you don't really need to use xsl:value-of with concat to output the text.
<xsl:template match="text()" name="createName">
<xsl:param name="Type"/>
<xsl:if test="contains($Type,'NEW')">
<xsl:text>New Goal</xsl:text>
</xsl:if>
<xsl:if test="contains($Type,'AMD')">
<xsl:text>Amended Goal</xsl:text>
</xsl:if>
</xsl:template>
Also note, you may not actually need the match attribute on the template either, if the template is only ever being called as a named template.
Also consider using xsl:choose / xsl:when instead of xsl:if.
You call this template in exactly the same way as shown in your question
<xsl:element name="PackageName">
<xsl:call-template name="createName">
<xsl:with-param name="Type" select="s0:PIXField/s0:TransactionID"/>
</xsl:call-template>
</xsl:element>
Nothing changes there at all.

How to call template from another template in xslt 1.0

I have two templates like this ...one to create normal package name and another to append "-Non" with the name if it passes certain conditions as shown
<xsl:template match="text()" name="checkForAgency">
<xsl:param name="Name"/>
<xsl:param name="Agency"/>
<xsl:if test="contains($Agency,'AG') ">
<xsl:value-of select="concat($Name,' - Non')"/>
</xsl:if>
</xsl:template>
<xsl:template match="text()" name="createPackageName">
<xsl:param name="Type"/>
<xsl:if test="contains($Type,'NEW')">
<xsl:value-of select="concat('New',' Goal')"/>
</xsl:if>
<xsl:if test="contains($Type,'AMD')">
<xsl:value-of select="concat('Amended',' Goal')"/>
</xsl:if>
<xsl:if test="contains($Type,'REN') or contains($Type,'ARN')">
<xsl:value-of select="concat('Renewal',' Goal')"/>
</xsl:if>
</xsl:template>
Calling these templates from
<xsl:variable name="NonAgencyPackageName">
<xsl:call-template name="createPackageName">
<xsl:with-param name="Type" select="ID"/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="PackageName">
<xsl:call-template name="checkForAgency">
<xsl:with-param name="Name" select="$NonAgencyPackageName"/>
<xsl:with-param name="Agency" select="Output"/>
</xsl:call-template>
</xsl:element>
If ID has 'New', the package name should be New Goal and if the Output has 'AG', then name should be New Goal- Non.
Else name should be new goal. I want it two separate templates only. I am getting empty value now. Please help me in achieving this and how to correct it?
At a guess, the XPaths ID and Output are not selecting what you expect, so all conditionals are failing. Since you don't provide any XML input, it's impossible to be sure.
Ask yourself several questions:
When this code is evaluated, what is the current element?
Does this element have a child element named ID?
What is the string value of that element?
Does it have a child element named Output?
What is its string value?
The PackageName element you generate will be non-empty only if there is an Output child and its string value contains the sequence 'AG'. It will contain the string 'New Goal - Non' only if the Output child contains 'AG' and the ID child contains NEW. No power in creation could make it have the value 'New Goal', since the template CheckForAgency either produces a value ending with ' - Non' or no output at all. (You may wish to read about the choose/when instruction in XSLT.)

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'"/>.