XSLT 1.0 adding values together from multiple nodes - xslt

Say I have the following XML
<A>100</A>
<B>200</B>
<C>300</C>
and the following XSLT
<TEST>
<xsl:value-of select="A + B + C"/>
</TEST>
It produces the following output
<TEST>600</TEST>
however, when one of the nodes is blank
<A></A>
<B>200</B>
<C>300</C>
I get the following.
<TEST>NaN</TEST>
I only want to add the nodes that are valid numbers. I could do this if xsl allowed me to dynamically replace a variable value by adding to the already existing variable, but even that would be messy. I assume there is an easy way that I'm missing?
I want XSLT 1.0 answers only please.
Thanks!

<TEST>
<xsl:value-of select="sum((A | B | C)[number(.) = .])"/>
</TEST>
That is, sum the subset of the elements A,B,C consisting of those whose contents can be used as numbers.
Note that number('') yields NaN, and (NaN = NaN) is false, so this will not include elements without text content.
We test for numbers as discussed at https://stackoverflow.com/a/3854389/423105

A little variation of LarsH's answer:
sum((A|B|C)[number(.) = number(.)])
the expression in the predicate doesn't cause any type error in XPath 2.0, because both arguments of = are of the same type -- xs:double.

There is also:
For XSLT 1.0:
sum((A|B|C)[string(number())!='NaN'])
For XSLT 2.0:
sum((A,B,C)[string(number())!='NaN'])
For interest, here is another one. It works for both XSLT 1.0 and 2.0
sum((A|B|C)[number()>-99999999])
In the above, replace -99999999 with one less than the lower bound of the possible values. This works because NaN > any-thing always returns false. This is probably the most efficient solution yet, but it may be seen as a little ugly.
For example, if you know for a fact that none of A, B or C will be negative values, you could put:
sum((A|B|C)[number()>0])
...which looks a little cleaner and is still quiet readable.

<xsl:value-of select="sum(/root/*[self::A or self::B or self::C][.!=''])"/>
This will add values from A, B, and C under the "Root" element so long as the value isn't blank.

Related

How to use contains() with a set of strings in XSLT

I have the following XML snippet:
<figure customer="ABC DEF">
<image customer="ABC"/>
<image customer="XYZ"/>
</figure>
I'd like to check if the figure element's customer attribute contains the customer attributes of the image elements.
<xsl:if test="contains(#customer, image/#customer)">
...
</xsl:if>
I get an error saying:
a sequence of more than one item is not allowed as the second argument of contains
It's important to note that I cannot tell the values of the customer attributes in advance, thus using xsl:choose is not an option here.
Is it possible to solve this without using xsl:for-each?
In XSLT 2.0 you can use:
test="image/#customer/contains(../../#customer, .) = true()"
and you will get a true() result if any of them are true. Actually, that leads me to suggest:
test="some $cust in image/#customer satisfies contains(#customer, $cust)"
but that won't address the situation where the customer string is a subset of another customer string.
Therefore, perhaps this is best:
test="tokenize(#customer,'\s+') = image/#customer"
... as that will do a string-by-string comparison and give you true() if any of the tokenized values of the figure attribute is equal to one of the image attributes.

xsl - select node by child

I have a problem selecting just elements of an xml, which contain a specific child node. Asumme the following part of an xml:
<root>
<Navision.Buchungen>
<Saldo>-110867.7500</Saldo>
<Navision.Kontostruktur>
<Bereich>1</Bereich>
</Navision.Kontostruktur>
</Navision.Buchungen>
<Navision.Buchungen>
<Saldo>-3082585.2100</Saldo>
<Navision.Kontostruktur>
<Bereich>2</Bereich>
</Navision.Kontostruktur>
</Navision.Buchungen>
...
</root>
Now I have an xsl part like this to get the sum of 'Saldo':
<xsl:variable name="FACT0" select="sum(//root/Navision.Buchungen/Saldo)"/>
But how can I select just the Saldo for 'Bereich' 1 for example?
Use this XPath:
//root/Navision.Buchungen[Navision.Kontostruktur/Bereich = 1]/Saldo
//root/Navision.Buchungen[Navision.Kontostruktur/Bereich = 1]/Saldo
Edited:
oh already posted.
For further problems you can use one of the online testbeds like this one. And of course good manuals like those from w3schools, also with testbeds for xsl

how to use two conditions in select conditions in xslt when using Apply template

<xsl:apply-templates mode="block2sequence" select="NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1] and NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][position()=1]"/>
why cant i use two conditions in above select condition, can any one suggest me
<xsl:apply-templates mode="block2"
select="NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1] "/>
why cant i use two conditions in above select condition
I guess this is to mean, "why can't the two conditions be specified in the same predicate?"
The answer is that the expression:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId and position() = 1]
isn't equivalent at all to the 1st expression above.
The first expression selects the first Table child of NewDataSet such that the string value of its CTD_CTD_PKG_ID child is equal to the string value of $PackageId. In this case we don't know which child (at which position) of NewDataSet will be selected -- any child that happens to be the first with the specified properties, will be selected.
On the other side, the latter expression selects the first Table child of NewDataSet only if the string value of its CTD_CTD_PKG_ID child is equal to the string value of $PackageId. In this case, if anything is selected, it would be the first Table child.
If you want an equivalent expression to the first one, that has only one predicate, one such expression is:
NewDataSet/Table
[CTD_CTD_PKG_ID =$PackageId
and
not(preceding-sibling::Table[CTD_CTD_PKG_ID =$PackageId ])
]
Update: The OP has published a code snippet:
<xsl:apply-templates mode="block2sequence" select=
"NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1]
and
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][position()=1]"/>
This code will cause an error thrown at compile time by the XSLT processor.
The value of the select attribute is a boolean (expr1 and expr2), however templates in XSLT 1.0 and XSLT 2.0 can only be applied on nodes. A boolean isn't a node -- hence the error.
Solution:
My first guess is that you want templates to be applied on both nodes. If this is so, then use:
<xsl:apply-templates mode="block2sequence" select=
"NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][1]
|
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][1]"/>
My second guess is that you want templates applied only on the first of the two nodes. If this is so, then use:
<xsl:apply-templates mode="block2sequence" select=
"(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
|
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType]
)
[1]
"/>
Notes:
Please, learn how to ask a question -- provide all relevant data and explain -- in the question, not in subsequent comments.
Did you know that [1] is equivalent to [position()=1] and is shorter?
You can use two conditions and your expression looks perfectly correct. If it is failing with an error, please tell us the error. If it is not selecting what you want, then (a) show us your source document, and (b) tell us what you want to be selected.
(You know, your question gives so little information, you don't give the impression that you really want an answer.)

xsl return the dynamic node value

hello:
do you guys know how to display the nodes' value which the nodes name are dynamic, for example, the nodes name is like x1, x2, x3... the number 1, 2 ,3 depends on the returns of the table.
i can get the node name using the loop, but only can get the name, even xsl:value-of select="$nodename", returns the nodename, not the value
As #Dimitre said, you haven't given us much specific information to work with, but in general you can use this to select elements whose names are determined at run time:
<xsl:value-of select="*[local-name() = $someDynamicValue]" />
You can also use name(), but local-name() ignores the namespace prefix, which usually makes things easier.
If you'd like more detailed help, please provide your sample input XML (especially the "returns of the table"), and the XSLT you've tried so far; and preferably, a sample of desired output XML.

XSLT: attribute value used as numeric predicate

Given
<xsl:variable name="datePrecision" as="element()*">
<p>Year</p>
<p>Month</p>
<p>Day</p>
<p>Time</p>
<p>Timestamp</p>
</xsl:variable>
The expression
$datePrecision[5]
returns a nodeSet containing one text node with value "Timestamp", as expected.
Later in a template, with a context element having an attribute
#precision="5"
I try the following expressions but all return an empty string:
$datePrecision[#precision]
$datePrecision[number(#precision)]
$datePrecision[xs:decimal(#precision)]
However, the following sequence does what I want
<xsl:variable name="prec" select="number(#precision)"/>
... $datePrecision[$prec] ...
Using Oxygen/XML's debugger I've stepped to the point where the expression is about to be evaluated and display the following in the watch window:
Expression Value Nodes/Values Set
-------------------------- --------------- -----------------------
$datePrecision[5] Node Set(1) #text Timestamp
#precision Node Set(1) precision 5
$datePrecision[#precision]
number(#precision) 5
$datePrecision[number(#precision)]
$prec 5
$datePrecision[$prec] Node Set(1) #text Timestamp
Obviously I've missed something fundamental about how attribute nodes are atomized for use in a predicate, but can't find anything in the docs (Michael Kay's XSLT/XPATH 2.0, 4th ed) that would explain this difference.
Can someone explain why this is occurring, and point me to where, in either the XSLT 2.0 spec or Michael Kay's book, where this is described?
(the XSLT processor is Saxon-PE 9.2.0.3)
Obviously I've missed something
fundamental
Yes. The XPath expression:
$datePrecision[#precision]
means: all elements in $datePrecision that have an attribute named precision.
But you want #precision to mean the attribute named precision of the currnet node that is matched by the template.
XSLT provides the current() function exactly for this purpose. Use:
$datePrecision[current()/#precision]
UPDATE: As Martin Honnen hinted, the OP probably wants to get the 5th element out of $datePrecision -- something not immediately visible from the description of the problem. In this case, it may be necessary to use:
$datePrecision[position() = current()/#precision]