Store output of a XSLT node into XSLT variable - xslt

My XSLT v1.0 code -
<Test1>
<xsl:text>"</xsl:text>
<xsl:choose>
<xsl:when test="/root/node1">B</xsl:when>
<xsl:when test="/root/node2">S</xsl:when>
<xsl:otherwise>NA</xsl:otherwise>
</xsl:choose>
<xsl:text>"</xsl:text>
</Test1>
I want to store the output of the above node <Test1> into a variable. Something like this,
<xsl:variable name="test">
<xsl:value-of select="??"/>
</xsl:variable>
Use the value of this variable to compute something else or display the value,
<Test2>
<xsl:text>"</xsl:text>
<xsl:value-of select="$test"/>
<xsl:text>"</xsl:text>
</Test2>
What should I write instead of ?? to get the value of the node <Test1>? Or is there any other way by which I can read the output value of a node into a variable in XSLT?

I think you simply want
<xsl:variable name="test">
<xsl:text>"</xsl:text>
<xsl:choose>
<xsl:when test="/root/node1">B</xsl:when>
<xsl:when test="/root/node2">S</xsl:when>
<xsl:otherwise>NA</xsl:otherwise>
</xsl:choose>
<xsl:text>"</xsl:text>
</xsl:variable>
and then
<Test2>
<xsl:text>"</xsl:text>
<xsl:value-of select="$test"/>
<xsl:text>"</xsl:text>
</Test2>
You could use an element node as the wrapper but it does not improve things if you simply want to deal with some strings like B or NA.

Related

idiomatic alternative to choose -> test -> value-of (XSLT 1.0)

In the work I do I seem to see a lot of code liek this..
<xsl:choose>
<xsl:when test="long_xpath_to_optional/#value1">
<xsl:value-of select="long_xpath_to_optional/#value"/>
</xsl:when>
<xsl:when test="another_long_xpath_to_optional/#value">
<xsl:value-of select="another_long_xpath_to_optional/#value"/>
</xsl:when>
<etc>
</etc>
<otherwise>
<xsl:value-of select="default_long_xpath_to_value"/>
</otherwise>
</xsl:choose>
its very long and very repetitive.
When I'm were working in some other (psuedo) language I would go
let values = concat(list(long_xpath_to_optional_value),list(another_long_xpath_to_optional_value))
let answer = tryhead(values,default_long_xpath_to_value)
i.e. create a list of values in priority order, and then take the head.
I only evaluate each path once
how would you do something similar in XSLT 1.0 (we can use node-sets).
I was wondering if you can create a node-set somehow
You can - but it's not going to be any shorter:
<xsl:variable name="values">
<xsl:apply-templates select="long_xpath_to_optional/#value" mode="values"/>
<xsl:apply-templates select="another_long_xpath_to_optional/#value" mode="values"/>
<xsl:apply-templates select="default_long_xpath_to_value/#value" mode="values"/>
</xsl:variable>
<xsl:value-of select="exsl:node-set($values)/value[1]" xmlns:exsl="http://exslt.org/common"/>
and then:
<xsl:template match="#value" mode="values">
<value>
<xsl:value-of select="."/>
</value>
</xsl:template>
But at least the repetition is eliminated.
Alternatively, you could do:
<xsl:template match="#value" mode="values">
<xsl:value-of select="."/>
<xsl:text>|</xsl:text>
</xsl:template>
and then:
<xsl:value-of select="substring-before($values, '|')"/>
To use variables you write
<xsl:variable name="value1" select="long_xpath_to_optional/#value1"/>
<xsl:variable name="value2" select="another_long_xpath_to_optional/#value"/>
<xsl:variable name="value3" select="default_long_xpath_to_value"/>
and then in XPath 2 or 3 all you would need is ($value1, $value2, $value3)[1] or head(($value1, $value2, $value3)) but in XSLT 1 with XPath 1 all you can write as a single expression is ($value1 | $value2 | $value3)[1] which sorts in document order so unless the document order is the same as your test order this wouldn't work to check the values; rather you would need to maintain the
<xsl:choose>
<xsl:when test="$value1">
<xsl:value-of select="$value1"/>
</xsl:when>
<xsl:when test="$value2">
<xsl:value-of select="$value2"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value3"/>
</xsl:otherwise>
</xsl:choose>
Of course in XPath 2 you wouldn't really need the variables and could use (long_xpath_to_optional/#value1, another_long_xpath_to_optional/#value, default_long_xpath_to_value)[1] as well directly.

Check if a map is empty in xslt

I create a map variable and want to check if it has items.
<xsl:variable as="map(xs:string, xs:string)*" name="ancestorsMap">
<xsl:map>
<xsl:for-each select="$nodes">
<xsl:variable name="ancestor" select="(ancestor::node()[not(descendant-or-self::layer)])[1]/#xml:id"/>
<xsl:if test="exists($ancestor)">
<xsl:map-entry key="string(#xml:id)" select="string($ancestor)"/>
</xsl:if>
</xsl:for-each>
</xsl:map>
</xsl:variable>
<xsl:variable name="check">
<xsl:choose>
<xsl:when test="empty($ancestorsMap)">
<xsl:value-of select="'NaN'"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$ancestorsMap"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
When it is empty, the oXygen variable panel shows:
Value type: map(*)1
Value: map{}
I tried fn:empty() so far
It looks like
test="map:size($ancestorsMap) = 0"
works fine. But maybe there are better approaches.

xsl:choose - Hardcoded 0's being added to end of XSLT string

I am having an issue that I cannot figure out in XLST where there are hardcoded 0's being added to the end of a string that I am not calling for. I am using a choose element to prompt placement of the string or to otherwise pick three 0's.
Can anyone tell in my code what I am doing wrong? See below:
<xsl:for-each select="Export/Record">
<xsl:if test="DebitAmount!=0 and DebitAmount!=''">
<xsl:value-of select="ChargedCorpLedgerCode" /><xsl:text>,</xsl:text>
<xsl:value-of select="DepartmentLedgerCode" /><xsl:text>,</xsl:text>
<xsl:value-of select="CategoryJournalNumber" /><xsl:text>,</xsl:text>
<xsl:value-of select="PFAM" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="LOC" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="ACTV" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="CLIENT"/><xsl:text> 0000000,</xsl:text>
<xsl:choose>
<xsl:when test="ProjectLedgerCode=null">
<xsl:value-of select="ProjectLedgerCode" /><xsl:text>,</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="ProjectLedgerCode" /><xsl:text> 000,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="DebitAmount" /><xsl:text>,</xsl:text>
<xsl:value-of select="''" /><xsl:text>,</xsl:text>
<xsl:value-of select="CategoryDesc" /><xsl:text>,</xsl:text>
<xsl:text>
</xsl:text>
</xsl:if>
my outcome looks like the below where the 000's are adding correctly when the column is blank, but when it is not, it adds the ProjectLedgerCode + 000
This test:
<xsl:when test="ProjectLedgerCode=null">
will return true if the string-value of ProjectLedgerCode is equal to the string-value of a sibling element named null.
If you want to test for ProjectLedgerCode not having a string-value, use:
<xsl:when test="ProjectLedgerCode=''">
or:
<xsl:when test="not(string(ProjectLedgerCode))">
In addition, I believe your results are mixed up.

How to check if node is empty

I'm trying to make a variable, which tells me, whether node1 has something in it or not. node1 can be an empty element or it may contain an attribute. But right now I would like to know how achieve answer "false" when the node1 is empty .
<xsl:variable name="elementHasData">
<xsl:choose>
<xsl:when test="node1 != ''">
<xsl:value-of select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Check whether node1 contains text or other child nodes:
<xsl:variable name="elementHasData" select="node1/node()" />
Check whether node1 contains child nodes or attributes:
<xsl:variable name="elementHasData" select="node1/node() or node1/#*" />
You can try this in XSLT 2.0:
<xsl:variable name="elementHasData" select="if (node1[node()]) then 'true()' else 'false()'"/>
<xsl:variable name="elementHasData">
<xsl:choose>
<xsl:when test="string-length(normalize-space(node1)) gt 0">
<xsl:value-of select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
If you need to check either any node or any attribute then you can go to this.
<xsl:variable name="elementHasData" select="if (node1[#* | node()]) then 'true()' else 'false()'"/>

Pass string into template parameter which expects node?

So,
I have an XSLT template which expects a node set as a parameter and uses this as display text. However, sometimes this node is empty in the XML and I want to pass default display text instead of the display text not showing up instead:
Works:
<xsl:call-template name="myTemplate">
<xsl:with-param name="parm1" select="//element">
</xsl:call-template>
Doesn't work:
<xsl:variable name="dispText">
<xsl:choose>
<xsl:when test="string-length(//element) = 0">
<xsl:value-of select="'Default Text'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//element" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="myTemplate">
<xsl:with-param name="parm1" select="$dispText">
</xsl:call-template>
Any ideas as to how I could accomplish this? I've tried all sorts of things with no luck :(
It seems like all I need to do is create a new node with the display text I want, but I don't know if that is even possible?
Thanks
Implement the default handling in the template, because that's where it belongs. The calling side should be consistent and not have side-effects on the template behavior (i.e. you should not be able to "forget" passing in the default value).
<xsl:template name="myTemplate">
<xsl:param name="parm1" /><!-- node set expected! -->
<!-- actual value or default -->
<xsl:variable name="value1">
<xsl:choose>
<xsl:when test="not($parm1 = '')">
<xsl:value-of select="$parm1" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default1" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- work with $value1 from this point on -->
</xsl:template>
I'm guessing //element is a nodeset and using string-length() on it might not be valid. Try converting it to a string() first?