XSLT: Check if a value exists in a list - xslt

So, I have a variable containing a nodeset with several Size nodes
<xsl:variable name="sizes" select="$filter/Size" />
I then, need to do a sum on another nodeset, where the Size/#ID exists in this $sizes variable
<xsl:value-of select="sum(Sizes/Size[ **where #ID in $sizes/#ID** ]/#Value)"/>
But I'm struggling on how I write this XPath...in xslt 1.0

<xsl:value-of select="sum(Sizes/Size[#ID = $sizes/#ID]/#Value)"/>
if I understand your spec correctly.
This works because of "existential quantification": A = B means "some member of node set A is equal to some member of node set B". (In your case, A has at most only one member anyway.)

Related

How to use an XSL variable as a condition to evaluate XPath?

I have faced an issue when using a variable as a condition for XPath evaluation. I have the following template which works fine:
<xsl:template name="typeReasonDic">
<xsl:variable name="dic" select="$schema//xs:simpleType[#name = 'type_reason_et']"/>
<!-- do something with the variable -->
</xsl:template>
However, when I change it to look like this:
<xsl:template name="typeReasonDic">
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
<xsl:variable name="dic" select="$schema//xs:simpleType[$choose_dic]"/>
<!-- do something with the variable -->
</xsl:template>
it fails to find the desired node.
What I wish to get is a template with a default value for $choose_dic which can be overriden where necessary.
What am I missing here?
UPD: there is this link I found with the description of what I'm trying to do, but it doesn't seem to work for me.
You can't do this directly in XSLT 1.0 or 2.0 without an extension function. The problem is that with
<xsl:template name="typeReasonDic">
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
<xsl:variable name="dic" select="$schema//xs:simpleType[$choose_dic]"/>
<!-- do something with the variable -->
</xsl:template>
the <xsl:param> will evaluate its select expression a single time in the current context and store the true/false result of this evaluation in the $choose_dic variable. The <xsl:variable> will therefore select either all xs:simpleType elements under the $schema (if $choose_dic is true) or none of them (if $choose_dic) is false. This is very different from
<xsl:variable name="dic" select="$schema//xs:simpleType[#name = 'type_reason_et']"/>
which will evaluate #name = 'type_reason_et' repeatedly, in the context of each xsl:simpleType, and select those elements for which the expression evaluated to true.
If you store the XPath expression as a string you can use an extension function such as dyn:evaluate or the XSLT 3.0 xsl:evaluate element if you're using Saxon.
By doing
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
the XSL engine will try to evaluate "#name = 'type_reason_et'" as an XPath expression, and will assign the RESULT to your variable.
You should use the following variable declaration instead:
<xsl:param name="choose_dic">#name = 'type_reason_et'</xsl:param>
This is the default value, but you can override it when you call your template by using xsl:with-param.
XSLT is not a macro language where you might be able to concatenate your code at run-time from strings and then evaluate them dynamically. So in general for your purpose you would need an extension function to evaluate an XPath expression stored in a string or you need to look into a new XSLT 3.0 features like http://www.saxonica.com/documentation/xsl-elements/evaluate.xml.
What is possible in the scope of XSLT 1.0 or 2.0 is doing e.g.
<xsl:param name="p1" select="'foo'"/>
<xsl:variable name="v1" select="//bar[#att = $p1]"/>
where the param holds a value you compare to other value, for instance those in a node like an attribute or element node.

Why do I have to assign this node to a variable in my stylesheet?

I'm working on an XSLT stylesheet. I have a a node (node A) with a bunch of children, and I'm looping through another node's (node B) children. I'm trying to do something each time a child of node B is also a child of node A, so I have this code:
<xsl:if test="$prodbins/bin[./text()=/root/Line[1]/Element[6]/text()]">
But that doesn't work (the test fails; the expression returns false) even though the left and right side of the expression, when evaluated separately, are equal.
But when I do this:
<xsl:variable name="curbin" select="/root/Line[1]/Element[6]/text()"/>
<xsl:if test="$prodbins/bin[./text()=$curbin]">
The expression evaluates to true. Why do I have to use the $curbin variable to get the result I'm expecting?
Can you try <xsl:if test="$prodbins/bin[./text()=current()/root/Line[1]/Element[6]/text()]"> (notice current() function). The reason why it does not work in your original expression is that because you query a variable and / looks up the root node of the content of the variable and not the source document you are transforming. current() should return the context element for the template you are in.
My guess is that $prodbins/bin is a node(-set) belonging to a different document than the document that contains the nodes that are being compared to.
In the expression:
$prodbins/bin[./text()=/root/Line[1]/Element[6]/text()]
the subexpression
/root/Line[1]/Element[6]/text()
selects from the same document as the one that is the document from which the $prodbins/bin nodes are selected.
One way to specify successfully the wanted comparisson is:
<xsl:variable name="vDoc" select="/"/>
<xsl:if test="$prodbins/bin[./text()=$vDoc/root/Line[1]/Element[6]/text()]">

msxsl:node-set() and position() are not working together

I've got an xsl:variable that contains a pre-processed set (DoesNotContainChildElement). I think msxsl:node-set() is adding a root element so position is always 1. But what I need it the top 15 elements.
<xsl:variable name="Summary">
<xsl:for-each select="msxsl:node-set($DoesNotContainChildElement)">
<xsl:if test="position() < 16">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
No, the function msxsl:node-set does not add any root node, it is simply that with XSLT 1.0 a sample like
<xsl:variable name="rtf1">
<item>1</item>
<item>2</item>
<item>3</item>
</xsl:variable>
creates a result tree fragment and "A result tree fragment is treated equivalently to a node-set that contains just a single root node". So in the sample above we have a result tree fragment with a single root node containing three item child elements.
Applying the msxsl:node-set(rtf1) extension function then gives you a node-set instead of a result tree fragment where now the node-set contains a single root node with three item child elements. Thus if you want to access the item elements you need msxsl:node-set($rtf1)/* or more general msxsl:node-set($rtf1)/node() to access all child nodes.
You may try msxsl:node-set($DoesNotContainChildElement)/*. If it's true that msxsl:node-set() adds a root node, then adding /* to your path will iterate over the children, where you can test for position.
Alternatively you could simply use <xsl:apply-templates select="msxsl:node-set($DoesNotContainChildElement)/*" mode="testposition"/> and <xsl:template match="*" mode="testposition">….
There's only one variable $DoesNotContainChildElement so a for-each will only yield value 1 for position().
You can check that by running the following:
<xsl:for-each select="$x">
<pos1><xsl:value-of select="position()"/></pos1>
</xsl:for-each>
<xsl:for-each select="$x/*">
<pos2><xsl:value-of select="position()"/></pos2>
</xsl:for-each>
Where x is a (node-set type) variable.
The result will look something like
<pos1>1</pos1>
<pos2>1</pos2>
<pos2>2</pos2>
<pos2>3</pos2>
<pos2>...</pos2>
Adding <xsl:copy-of select="."/> will result in the output of the entire variable contents in case of the first for-each above, whereas for the second for-each it will result in the output of each sub-element of the variable one-by-one.
The second form is the one to use if you wish to output only selected sub-elements.
The same holds when you first apply the node-set function to change an rtf into a node-set.

How to know variable has value or not in XSLT

I am creating XSLT file.
I have one variable which take value from XML file.But it may happen that there is no reference in xml for the value and at that time XSL variable will return False/None(don't know).I want keep condition like,If there is no value for the variable use the default one.
How to do that ?
With the few details given in the question, the simplest test you can do is:
<xsl:if test="$var">
...
</xsl:if>
Or you might use xsl:choose if you want to provide output for the else-case:
<xsl:choose>
<xsl:when test="not($var)"> <!-- parameter has not been supplied -->
</xsl:when>
<xsl:otherwise> <!--parameter has been supplied --> </xsl:otherwise>
</xsl:choose>
The second example will also handle the case correctly that the variable or parameter has not been supplied with an actual value, i.e. it equals the empty string. This works because not('') returns true.
You haven't explained what you mean by "has no value". Here is a generic solution:
not($v) and not(string($v))
This expression evaluates to true() iff $v "has no value".
Both conditions need to be met, because a string $v defined as '0' has a value, but not($v) is true().
In XSLT 1.0 using a default can be achieved in different ways if the "value" is a node-set or if the value is a scalar (such as a string, a number or a boolean).
#Alejandro provided one way to get a default value if a variable that is supposed to contain a node-set is empty.
If the variable is supposed to contain a scalar, then the following expression returns its value (if it has a value) or (otherwise) the desired default:
concat($v, substring($default, 1 div (not($v) and not(string($v)))))
You can use string-length to check, if a variable called $reference for example contains anything.
<xsl:choose>
<xsl:when test="string-length($reference) > 0">
<xsl:value-of select="$reference" />
</xsl:when>
<xsl:otherwise>
<xsl:text>some default value</xsl:text>
</xsl:otherwise>
</xsl:choose>
If necessary use normalize-space, too.
First, all variables has values, because XSLT belongs to declarative paradigm: there is no asignation instruction, but when you declare the variable you are also declaring the expression for its value relationship.
If this value it's a node set data type (that looks from your question), then you should test for an empty node set in case nothing was selected. The efective boolean value for an empty node set is false. So, as #0xA3 has answered: test="$node-set".
You wrote:
If there is no value for the variable
use the default one. How to do that ?
Well, that depends on what kind of data type you are looking for.
Suppose the node set data type: if you want $node-set-1 value or $node-set-2 if $node-set-1 is empty, then use:
$node-set-1|$node-set-2[not($node-set-1)]
I tried a lot of solution from SO, my last solution was taken from #dimitre-novatchev, but that one also not working every time. Recently I found one more solution from random google search, thought to share with the community.
In order to check empty variable value, we can declare an empty variable and compare its value against test condition. Here is code snippet:
<xsl:variable name="empty_string"/>
<xsl:if test="testVariableValue != $empty_string">
...
</xsl:if>
Here testVariableValue hold the value of new variable to be tested for empty
scenario.
Hope it would help to test empty variable state.

Determining whether a node is contained within another node in XSLT

Is it possible to tell whether a node is contained within (or equal to) another node in XSLT? For example, consider this code snippet:
<xsl:variable name="itemSection" select=".."/>
<xsl:for-each select="key('enemyItems', #key)">
<xsl:variable name="enemyList" select="./attributes/#value"/>
<xsl:variable name="enemyListSection" select="../../.."/>
.
.
.
</xsl:for-each>
Is it possible to tell whether itemSection is contained within (or equal to) enemyListSection?
In XPath 1.0
$itemSection[ancestor::*[generate-id()=generate-id($enemyListSection)]]
In XPath 2.0
$itemSection[ancestor::*[. is $enemyListSection]]
Just a small adjustment to Alejandro's answer:
In XPath 1.0
$itemSection[ancestor-or-self::*[generate-id()=generate-id($enemyListSection)]]
In XPath 2.0
$itemSection[ancestor-or-self::*[. is $enemyListSection]]
Because the original question asked:
Is it possible to tell whether
itemSection is contained within (or
equal to) enemyListSection?