XPATH: How to use a variable in call-template - xslt

I am writing an XSLT to transform an XML: I am not experienced in Xpath, and my XML is too big to post.
But here is my problem:
This code: works fine:
<cac:testmathieu>
<xsl:variable name="lineAmount2" select="body:LineAmount * -1" />
<xsl:value-of select="../body:TaxTrans[body:TaxBaseAmount=$lineAmount2]/body:TaxAmount"/>
</cac:testmathieu>
Then, why is the code below not working, when it's used right below the above code:
<xsl:call-template name ="TaxTotalLine">
<xsl:with-param name="TaxAmount" select="../body:TaxTrans[body:TaxBaseAmount=body:LineAmount*-1]/body:TaxAmount"></xsl:with-param>
</xsl:call-template>
The second piece of code is just a merge of of the code in the first example, or am I mistaken?

I think you want
<xsl:call-template name ="TaxTotalLine">
<xsl:with-param name="TaxAmount" select="../body:TaxTrans[body:TaxBaseAmount = current()/body:LineAmount*-1]/body:TaxAmount"></xsl:with-param>
</xsl:call-template>
to compare the LineAmount of the currently processed node (e.g. any outer template or for-each) to the TaxBaseAmout of the TaxTrans element you have applied the predicate in square brackets to.

thanks for the response.
It was acutally a combination of the "current()" and the brackets.
Much appreciated !
Kind regards
Mathieu

Related

XSLT Check if case occured in for-each and use the information

I don't have an approach for this, since I'm not able to change values of variables in XSLT.
Hopefully you could provide a proper approach for the following problem:
This is an example xml-node, to which I'll quote to explain the problem:
<information>
<uselessNode1></uselessNode1>
<uselessNode2></uselessNode2>
<importantNode></importantNode>
</information>
What I essentially need here is to go through all the child nodes of the information node.
The importantNode is optional and the thing what I've to detect is whether this node is there or not.
If the importantNode is there it'll processed within the for-each loop and everything is fine.
In case of not having the importandNode, I've to show it's absence outside of the for-each loop.
So, if having following for-each loop in a template for the information node:
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="not( name() = 'importantNode')">
USELESS
</xsl:when>
<xsl:otherwise>
IMPORTANT
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
The output is "USELESS USELESS IMPORTANT". And if the important node is not there the output is
"USELESS USELESS". What I actually need to have instead of "USELESS USELESS" is a prompt, which shows that the important node is not there, e.g. "USELESS USELESS IMPORTANT_MISSING".
I tried to set a variable in the for-each loop if going in the -case and trying to
check this outside of the for-each loop throws an error, that the variable $importantFound is not declared or out of it's scope. I though declaring it before the for-each loop would help. Since I can't declare the variable before the for-each loop and change it's value in the otherwise case inside of the loop, I don't know how to solve this dilemma.
I hope the explanation of my problem was clear enough to understand.
Thank You all in advance.
In case of not having the importandNode, I've to show it's absence outside of the for-each loop.
So why can't you do exactly that - for example:
<xsl:template match="information">
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="not(self::importantNode)">USELESS </xsl:when>
<xsl:otherwise>IMPORTANT </xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:if test="not(importantNode)">IMPORTANT_MISSING</xsl:if>
</xsl:template>

Looping through an xpath getting the right iteration each time in xslt 2

I have a condition where I need to loop atleast once and so I have the following xsl code. However, this doesnt work as it always gets the last iterations value. How can I tweak this so it gets the right iteration on each loop?
<xsl:variable name='count0'>
<xsl:choose>
<xsl:when test='count($_BoolCheck/BoolCheck[1]/CheckBoolType) = 0'>
<xsl:value-of select="1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='count($_BoolCheck/BoolCheck[1]/CheckBoolType)'/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:for-each select="1 to $count0">
<xsl:variable name='_LoopVar_2_0' select='$_BoolCheck/BoolCheck[1]/CheckBoolType[position()=$count0]'/>
<e>
<xsl:attribute name="n">ValueIsTrue</xsl:attribute>
<xsl:attribute name="m">f</xsl:attribute>
<xsl:attribute name="d">f</xsl:attribute>
<xsl:if test="(ctvf:isTrue($_LoopVar_2_0/CheckBoolType[1]))">
<xsl:value-of select=""Value True""/>
</xsl:if>
</e>
</xsl:for-each>
The xml file is as follows:
<BoolCheck>
<CheckBoolType>true</CheckBoolType>
<CheckBoolType>false</CheckBoolType>
<CheckBoolType>1</CheckBoolType>
<CheckBoolType>0</CheckBoolType>
<CheckBoolType>True</CheckBoolType>
<CheckBoolType>False</CheckBoolType>
<CheckBoolType>TRUE</CheckBoolType>
<CheckBoolType>FALSE</CheckBoolType>
</BoolCheck>
In this case I need to iterate through each iteration of CheckBoolType and produce a corresponding number of values. However, in the above example if there were no CheckBoolType iterations I would still like the iterations to enter the for-each loop atleast once. i hope that clarifies it a little more.
First observation: your declaration of $count0 can be replaced by
<xsl:variable name="temp" select="count($_BoolCheck/BoolCheck[1]/CheckBoolType)"/>
<xsl:variable name="count0" select="if ($temp=0) then 1 else $temp"/>
(Sorry if that seems irrelevant, but my first step in debugging code is always to simplify it. It makes the bugs much easier to find).
When you do this you can safely replace the predicate [position()=$count0] by [$count0], because $count0 is now an integer rather than a document node. (Even better, declare it as an integer using as='xs:integer' on the xsl:variable declaration.)
But hang on, $count0 is the number of elements being processed, so CheckBoolType[$count] will always select the last one. That's surely not what you want.
This brings us to another bug in your code. The value of the variable $_LoopVar_2_0 is an element node named CheckBoolType. The expression $_LoopVar_2_0/CheckBoolType[1] is looking for children of this element that are also named CheckBoolType. There are no such children, so the expression selects an empty sequence, so the boolean test is always false.
At this stage I would like to show you some correct code to achieve your desired output. Unfortunately you haven't shown us the desired output. I can't reverse engineer the requirement from (a) your incorrect code, and (b) your prose description of the algorithm you are trying to implement.

How can I shorten this XSLT snippet?

I have to repeat the following XSLT snippet like 100 times and I would like it to be as small as possible. Is there a way to make an equivalent XSLT snippet that is shorter?
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:choose>
<xslo:when test="string($myVariable) != 'NaN'">
<xslo:text>1</xslo:text>
</xslo:when>
<xslo:otherwise>
<xslo:text>0</xslo:text>
</xslo:otherwise>
</xslo:choose>
I'm basically setting the state of a checkbox based on whether or not a value exists in //This/that/anotherthing in the source xml.
Can be XSLT 1.0 or XSLT 2.0, doesn't matter.
You can use an if instead of xsl:choose (XSLT 2.0 only)...
<xsl:value-of select="if (string(number(//This/that/anotherthing)) = 'NaN') then 0 else 1"/>
I also dropped the xsl:variable, but if you need it for some other reason, you can put it back.
You could also create a function...
<xsl:function name="local:isNumber">
<xsl:param name="context"/>
<xsl:value-of select="if (string(number($context)) = 'NaN') then 0 else 1"/>
</xsl:function>
usage...
<xsl:value-of select="local:isNumber(//This/that/anotherthing)"/>
<xslo:variable name="myVariable" select="//This/that/anotherthing" />
<xslo:value-of select="number(boolean($myVariable))"/>
If I understand the purpose correctly - that is return 1 if the value in question can be successfully expressed as a number, 0 otherwise - then I believe:
<xsl:value-of select="number(//This/that/anotherthing castable as xs:double)"/>
would be the most straightforward way (in XSLT 2.0) to achieve it.
Edit
In view of your change of purpose:
I'm basically setting the state of a checkbox based on whether or not
a value exists in //This/that/anotherthing
That's even simpler:
<xsl:value-of select="number(boolean(string(//This/that/anotherthing)))"/>

Creating a variable number of nodes on target document without corresponding data on source document

I'm trying to map two documents witht the BizTalk Mapper and my target document should look like this:
<root>
<complexType>
<property>example</property>
</complexType>
<filler>
<padding>9999999</padding>
</filler>
<filler>
<padding>9999999</padding>
</filler>
<filler>
<padding>9999999</padding>
</filler>
</root>
The number of <filler> nodes that I should create is variable (from 0 to 9). It is basically the result of a calculation (based on some data provided in the source document).
Is there a way to create those <filler> nodes with some combination of functoids?
I tried to use the Table Looping functoid (created a table with only one column, the padding char '9') but doesn't really work because it creates as many <filler> nodes as rows are defined in the table, which is not what I want since the number of rows would have to be variable (again, based on a calculation).
What I currently do is pass the message (XmlDocument) to a C# method and then I programmatically append the <filler> nodes.
I'm hoping that there is a more "BizTalk-y" way of doing this with the Mapper.
I suspect that you will have to solve this problem by altering the XSLT.
Add some logic to create as many filler nodes as the result of your calculation dictates - you could create a template which you call in a loop perhaps, which would append a new filler section.
Hope this points you in the right direction.
As pointed out, XSLT can create nodes on the target document at will (I didn't know this and this was the key part).
Turns out that what I needed is a simple for-loop in XSLT. Once I realized this, a quick Google search yielded the following results:
http://quomon.com/question-How-to-make-a-for-loop-in-xslt-not-for-each-809.aspx
http://snippets.dzone.com/posts/show/930
Another thing worth noting is that (as pointed out by the first link), XSLT is a functional language, not procedural, so sometimes you do have to resort to using recursion or an extension.
This case is definitely one of those times since I couldn't use a careful selection of nodes using the select attribute on an xsl:for-each (since this filler data wasn't part of the source document).
Specifically, for this case, what I did was:
Add a Scripting functoid.
Add two inputs:
A constant with value "1" (this is the initial value of the i variable)
The length of the loop (number of times to repeat the body of the loop)
Paste the following XSLT template as an "Inline XSLT Call Template" script:
<xsl:template name="ForLoop">
<xsl:param name="i" /> <!-- index counter, 1-based, will be incremented with every recursive call -->
<xsl:param name="length" /> <!-- exit loop when i >= length -->
<!-- Output the desired node(s) if we're still looping -->
<!-- The base case is when i > length (in that case, do nothing) -->
<xsl:if test="$i <= $length">
<Filler>
<Padding>999999</Padding>
</Filler>
</xsl:if>
<!-- Call the ForLoop template recursively, incrementing i -->
<xsl:if test="$i <= $length">
<xsl:call-template name="ForLoop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="length">
<xsl:value-of select="$length"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>

Adding of two variables in xslt within choose statement

<xsl:choose>
<xsl:when test="$cty='LOHNSTD'">
<xsl:variable name="sum" select="$sum + $amt"/>
</xsl:when>
<xsl:when test="$cty='REGPAY'">
<xsl:variable name="sum1">
<xsl:value-of select="$sum1 + $amt"/>
</xsl:variable>
</xsl:when>
</xsl:choose>
In the above code it gives me warning message saying variables sum and sum1 are not declared. amt and cty are parameters being passed to this template. Can any one help me in doing the summation based on different category codes?
As already mentioned, you have two problems with your stylesheet fragment: ussing an undeclare variable reference (xsl:variable name="sum" select="$sum + $amt"), and loosing the variable scope outside the xsl:when.
If you want to declare a variable with value deppending on some conditions, then the answer from Ledhund is the right choise: use xsl:choose inside variable's content template.
Also, if the terms of sum are node sets, you could use this expression:
sum($A|$B[$cty='LOHNSTD']|$C[$cty='REGPAY'])
Or
$A + $B[$cty='LOHNSTD'] + $C[$cty='REGPAY']
If you are trying to chance the value of an already declared variable, then you should refactor your transformation because that's not posible in any declarative paradigm as XSLT.
If you can live without having two sum-variables I'd do it like this
<xsl:variable name="sum">
<xsl:choose>
<xsl:when test="$cty='LOHNSTD'">
<xsl:value-of select="$something + $amt"/>
</xsl:when>
<xsl:when test="$cty='REGPAY'">
<xsl:value-of select="$something_else + $amt"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
However if the main thing is to increase the value of variables depending on some condition, you can't. Variables are fixed.
First, you cannot declare a variable inside a choose construct and use it elsewhere. A variable with no following sibling instructions is useless.
In your example you have two problems, the one mentioned above, and that you're trying to use the variables $sum and $sum1 before they're declared (just as the error message suggests). Essensially, you're trying to calculate a sum using the variable you are declaring.
I'm not sure what you're trying to accomplish, but if you give us some more information regarding the problem I'm sure we can help you with a better solution to it.