How to check if node is empty - xslt

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()'"/>

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.

XPath addressing of UML stereotype inside XMI file

I have a problem with XSLT/XPath when parsing my UML file (XMI).
The following expressions behave differently ($thisId contains the xmi:id of the class to check whether it is enhanced by the stereotype 'revisioned' inside 'itechnical_Profile'):
1)
<xsl:value-of select="//*:revisioned[#base_Class = $thisId]/#xmi:id"/>
gives me correct id '_16_5_1_29f0058e_1359051179028_158208_16339application2'.
2)
<xsl:value-of select="//itechnical_Profile:revisioned[#base_Class = $thisId]/#xmi:id"/>
gives me no output at all.
3)
<xsl:value-of select="/xmi:XMI/*:revisioned[#base_Class = $thisId]/#xmi:id"/>
gives me correct id '_16_5_1_29f0058e_1359051179028_158208_16339application2'.
I have checked with
<xsl:value-of select="name(/xmi:XMI/*:revisioned[#base_Class = $thisId])"/>
that the node is named 'itechnical_Profile:revisioned'.
I have also checked with
<xsl:value-of select="name(/xmi:XMI/*:revisioned[#base_Class = $thisId]/..)"/>
that the parent node is named 'xmi:XMI'.
So why does 2) fail?
With the stereotype 'UML_Standard_Profile:entity' it works flawlessly.
Is my understanding of '//' wrong?
Thanks in advance!
Shortening the XMI file would be quite some work ... so after some debugging I have come up with the following workaround:
<xsl:function name="itechnical_uml:enhancedByStereotype_revisioned" as="xs:boolean">
<xsl:param name="packagedElementNode" as="element()"/>
<xsl:variable name="elementName" select="local-name($packagedElementNode)"/>
<xsl:if test="($elementName != 'packagedElement')">
<xsl:message terminate="yes">
Error: this is not a packagedElement!
</xsl:message>
</xsl:if>
<xsl:if test="($packagedElementNode/#xmi:type != 'uml:Class')">
<xsl:message terminate="yes">
<xsl:text>Error: this is a packagedElement but not with xmi:type = 'uml:Class'!
</xsl:text>
</xsl:message>
</xsl:if>
<xsl:variable name="thisId" select="$packagedElementNode/#xmi:id"/>
<xsl:variable name="stereotypeID">
<xsl:value-of select="root($packagedElementNode)//stereotype[#name = 'itechnical_Profile:revisioned']/#stereotypeID"/>
</xsl:variable>
<xsl:if test="$stereotypeID = ''">
<xsl:message terminate="yes">
<xsl:text>Error: unable to find xmi:id of stereotype 'itechnical_Profile:revisioned'!
</xsl:text>
</xsl:message>
</xsl:if>
<xsl:variable name="resultSet" as="element()*" select="root($packagedElementNode)/xmi:XMI/*:revisioned[#base_Class = $thisId]"/>
<xsl:choose>
<xsl:when test="$resultSet/#xmi:id != ''">
<xsl:value-of select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
Checking the 'xmlns:itechnical_Profile' definitions inside UML XMI and my XSLT scripts did not show up any problems.

Store output of a XSLT node into XSLT variable

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.