How to get value of a child of node-set? - xslt

I am using a variable in my XSLT to select correct node and then I want to use this variable to retrieve its child nodes:
<xsl:variable name="CorrectNode">
<xsl:choose>
<xsl:when test="$Formula='14'">
<xsl:value-of select="f:node14" />
</xsl:when>
<xsl:when test="$Formula='15'">
<xsl:value-of select="f:node15" />
</xsl:when>
</xsl:choose>
</xsl:variable>
<Revenue>
<xsl:value-of select="msxsl:node-set($CorrectNode)/f1:revenueValue" />
</Revenue>
However, it does not output anything. If I have:
<xsl:value-of select="msxsl:node-set($CorrectNode)" />
or
<xsl:copy-of select="msxsl:node-set($CorrectNode)" />
then the values or nodes are outputted but how to access its children?

This snippet sets the variable to a result tree fragment consisting of one text node, whose value is the string value of the appropriate element. The variable does not refer to the element itself, only its string value.
But you don't need the RTF and node-set function here at all, as you can do it with a straight select using appropriate predicates, since the test expressions are not context-dependent (a test like $Formula = '15' gives the same value regardless of whether the current context is the f:node15 element or its parent):
<xsl:variable name="CorrectNode"
select="f:node14[$Formula = '14'] | f:node15[$Formula = '15']" />
This way the variable is a reference to the original element, and you can navigate from there using XPath.

Related

xslt in xml attribute

I have xml element tyle boleean.
<testelement>0</testelement>
I use xslt to transform value to no/yes depending on 0/1 value and it works great
<xsl:choose>
<xsl:when test="./text()='0'">
<xsl:text>No</xsl:text>
</xsl:when>
<xsl:when test="./text()='1'">
<xsl:text>Yes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">The Yes/No value to be translated did not match expected input</xsl:message>
</xsl:otherwise>
</xsl:choose>
The same I try to do with attribute type boolean. Element has maxOcc unbounded.
<element attribute="0">
...
</element>
<element attribute="1">
...
</element>
In xlts:
<xsl:choose>
<xsl:when test="//#attribute='0'">
<xsl:text>No</xsl:text>
</xsl:when>
<xsl:when test="//#attribute='1'">
<xsl:text>Yes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="no">The Yes/No value to be translated did not match expected input</xsl:message>
</xsl:otherwise>
</xsl:choose>
But after i use this code all values are Yes or all values are No depending what is value in first node element. EG if 0 is in first element all values are No and it doesnd matter that in second is 1.
How to transform it properly?
Thanks
all values are Yes or all values are No
depending what is value in first node element
Yes, of course they are. That's because your test:
<xsl:when test="//#attribute='0'">
selects all the attributes in the XML document, and in XSLT 1.0 (which I assume you're using) only the first one's value will be used.
You need first to be in the context of element, then test that specific element's attribute by:
<xsl:when test="#attribute='0'">
Here's a better way to do it:
<xsl:template match="node()[.='0'] | #*[.='0']" mode="toYesNo"/>No</xsl:template>
<xsl:template match="node()[.='1'] | #*[.='1']" mode="toYesNo"/>Yes</xsl:template>
<xsl:template match="node()|#*" mode="toYesNo"/>
<xsl:message terminate="no">The Yes/No value to be translated did not match expected input</xsl:message>
</xsl:template>
and then you can xsl:apply-templates (with mode="toYesNo") selecting any element, attribute, or text node to get the appropriate conversion.
In XSLT 3.0 you can replace the patterns withm for example, match=".[.='0']" to match any kind of node.
Try to avoid using ./text() because it goes wrong when there are comments in your XML. You can nearly always replace it with ..
And of course you error with the attributes was the leading //. You need to be very clear about the difference between absolute path expressions (which start with / and select from the root of the tree) and relative path expressions (which select from the node you are current processing).

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.)

XSLT 1.0: if statement when position matches a variable value

I'm trying to insert an IF statement into an existing FOR-EACH loop to do something slightly different if it matches a variable from another node (the node I want is actually a sibling of it's parent - if that makes sense!?).
The value is a simple integer. I basically want to say: If the position is equal to the variable number then do XXXX.
Here is the XSLT, it's only v1.0 and not 2.0 that I can use.
<xsl:for-each select="/Properties/Data/Datum[#ID='ID1']/DCR[#Type='accordion_tab']/accordion_tab/sections">
<h3 class="accordionButton">
<xsl:if test="position()='openpane value to go here'">
<xsl:attribute name="class">
<xsl:text>new text</xsl:text>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
My XML extract is here:
<sections>
<title>title</title>
<text>some text</text>
</sections>
<openpane>2</openpane>
You didn't make this clear in your question, but I assume you iterate over the sections elements in your for-each loop. From the for-each loop you can reach the openpane element by going through the parent of the current sections element:
<xsl:for-each select="sections">
<xsl:if test="position() = ../openpane">
...
</xsl:if>
</xsl:for-each>
You could also define a variable referring to the openpane element first:
<xsl:variable name="openpane" select="openpane"/>
<xsl:for-each select="sections">
<xsl:if test="position() = $openpane">
...
</xsl:if>
</xsl:for-each>

xslt assigning variable value to string literal

I have
<xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="expression" select="name()" ></xsl:variable>
</xsl:for-each>
below I have href and i want to set the value this expression variable in href #######
Delete
I also tried with :
Delete
None of them worked.
Can Anybody help me out how to do that?
Basically my task is to find the expression for current node and send the expression for the same in href?
Adding More Info :
<br/><xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="expression" select="name()" />
</xsl:for-each>
<b>Click Me</b>
when above xsl code is transformed it is giving below error:
Variable or parameter 'expression' is undefined.
In XSLT1.0, you could try setting the variable like so:
<xsl:variable name="expression">
<xsl:for-each select="ancestor-or-self::*">
<xsl:text>/</xsl:text>
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="$name"/>
</xsl:for-each>
</xsl:variable>
So, assuming the following XML structure
<As>
<a>
<Bs>
<b>5</b>
</Bs>
</a>
<a>
<Bs>
<b>9</b>
</Bs>
</a>
<a/>
<a>
<Bs>
<b>12</b>
<b>14</b>
<b>15</b>
</Bs>
</a>
</As>
If you were positioned on the b element with the value of 14, then expression would be set to /As/a/Bs/b
However, this does not take into account multiple nodes with the same name, and so would not be sufficient if you wanted accurate XPath to select the node.
Instead, you could try the following:
<xsl:variable name="expression">
<xsl:for-each select="ancestor-or-self::*">
<xsl:text>/</xsl:text>
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="$name"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name() = $name]) + 1"/>
<xsl:text>]</xsl:text>
</xsl:for-each>
</xsl:variable>
This would return /As[1]/a[4]/Bs[1]/b[2], which may be what you want.
Very simply, variables are scoped in XSL and exist only within the containing tag. Thus the variable named expression exists only within the for-each block. Also, variables can only be set once. Attempting to set a variable value a second time has no effect.
Therfore you have to declare the variable at or above the level where you want to use it, and put all the code to generate the value inside the variable declaration. If you can use XSLT2, the following will do what you want:
string-join(for $n in ancestor-or-self::* return name($n), '/')
I know it can also be done in XSLT 1 with a recursive template, but I don't have an example handy.

XSl:Variable - Condition to check whether value exist

Using XSLT 1.0, how do I check whether the value in the variable exists or not?
I am assigning the value to the variable initially from my XML data and then need to check whether it exits or not:
<xsl:variable name="DOC_TYPE">
<xsl:value-of select="name(./RootTag/*[1])"/>
</xsl:variable>
<xsl:if test="string($DOC_TYPE) = ''">
<xsl:variable name="DOC_TYPE">
<xsl:value-of select="name(./*[1])"/>
</xsl:variable>
</xsl:if>
The above is not working as expected. What I need is if <RootTag> exists in my data then the variable should contain the child node below the <RootTag>. If <RootTag> does not exist then the DOC_TYPE should be the first Tag in my XML data.
Thanks for your response.
You can't re-assign variables in XSLT. Variables a immutable, you can't change their value. Ever.
This means you must decide within the variable declaration what value it is going to have:
<xsl:variable name="DOC_TYPE">
<xsl:choose>
<xsl:when test="RootTag">
<xsl:value-of select="name(RootTag/*[1])" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name(*[1])" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
A few other notes:
this: './RootTag' is redundant. Every XPath you don't start with a slash is relative by default, so saying 'RootTag' is enough
this: <xsl:value-of select="name(*[1])"/> already results in a string (names are strings by definition), so there is no need to do <xsl:if test="string($DOC_TYPE) = ''"> , a simple <xsl:if test="$DOC_TYPE = ''"> suffices
to check if a node exists simply select it via XPath in a test="..." expression - any non-empty node-set evaluates to true
XSLT has strict scoping rules. Variables are valid within their parent elements only. Your second variable (the one within the <xsl:if>) would go out of scope immediately(meaning right at the </xsl:if>).
Try this
<xsl:variable name="DOC_TYPE">
<xsl:choose>
<xsl:when test="/RootTag"><xsl:value-of select="name(/RootTag/*[1])"></xsl:value-of></xsl:when>
<xsl:otherwise><xsl:value-of select="name(/*[1])"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
It only exists if you have assigned it. There's no reason to test it for existence.
See also here