7

I am making use of XSLT and XML to produce an output document.

What I have in the data (in XML form over which I have no control) is the following:

<ea type="null"/>
<pa type="null"/>
<perf>4</perf>

I need to use these in calculations. I see that providing a default value for these requires performing a transformation on the document to provide a default value which is a bit long winded.

So I thought, does there exist a ternary operation in XPath/XSLT, along the lines of PHP:

$result = ($var == null ? true:false)

So that the following provides a result (even if it is zero)

<xsl:value-of select="ea + pa + perf" />
Jens Erat
  • 37,523
  • 16
  • 80
  • 96
swshaun
  • 384
  • 1
  • 4
  • 12
  • 1
    Why on earth would you ever do `expr ? true : false`? Just do `expr` instead. – Billy ONeal Apr 09 '13 at 21:43
  • Sorry that is just an example. If it is empty, I want it to be 0... If it isn't then just the nodes value. – swshaun Apr 09 '13 at 21:44
  • 1
    I think it's time to stop using "XSLT" to mean "XSLT 1.0". I know that people in the PHP world are still stuck with XSLT 1.0, but a lot of other people have moved on, and if your question relates specifically to the old version you should make this clear. – Michael Kay Apr 09 '13 at 23:00
  • You may be interested to know that the accepted answer doesn't even compile with any true XSLT 1.0 processor -- see example of the produced error at the end of my answer. – Dimitre Novatchev Apr 10 '13 at 02:33
  • Thank you folks -- I shall be more specific in future. – swshaun Apr 10 '13 at 18:17

5 Answers5

5

XPath does not support a conditional expression operator. It does, however, support if/then/else syntax if you happen to have an XSLT 2.0 processor. (Note that most XSLT processors do not support this)

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • Thank you for this -- I am using XSLT 1.0. I spotted the if/then/else and also choose/when/otherwise syntax. I shall have to perform to transformations on my XML -- one to supply default values to the empty nodes, then a second to perform the work I want. – swshaun Apr 09 '13 at 21:47
  • @swshaun: If you are using XSLT 1.0 then you have no choice but to create ``s and use `` to populate them. – Billy ONeal Apr 09 '13 at 22:05
  • @swshaun, Of course what Billy ONeal tells you in his comment is **not** true. See my answer for a single XPath 1.0 expression that calculates exactly the wanted sum... :) – Dimitre Novatchev Oct 25 '20 at 20:53
2

Use this XPath one-liner:

sum(((ea|pa)/@type | perf)[number()= number()])

XSLT 1.0 - based verification:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:copy-of select="sum(((ea|pa)/@type | perf)[number()= number()]) "/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document (the provided fragment, wrapped into a single top element):

<t>
    <ea type="null"/>
    <pa type="null"/>
    <perf>4</perf>
</t>

The XPath expression is evaluated (using as initial context node the top element) and the result of this evaluation is copied to the output:

4

If all elements' values are non-numbers, such as in:

<t>
    <ea type="null"/>
    <pa type="null"/>
    <perf>I am a string</perf>
</t>

the result is again correct:

0

Do note:

The currently selected answer doesn't contain a syntactically valid XSLT 1.0 transformation and any XSLT 1.0 processor (not an XSLT 2.0 one) produces error like this (with Saxon 6.5.4):

SAXON 6.5.4 from Michael Kay
Java version 1.6.0_31
Error at xsl:copy-of on line 12 of file:/(Untitled):
  Error in expression sum(   (     //ea[not(@type eq 'null')],     //pa[not(@type eq 'null')],     //perf[not(@type eq 'null')]   ) ) : expected ")", found "<name>"
Transformation failed: Failed to compile stylesheet. 1 error detected.
Press any key to continue . . . 
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
1

Depending on how complex you need, there are a couple good approaches ( the 2nd is from here ).

Approach #1 exslt:node-set('default value') | @IfThisNodeExists says that if the 2nd node exists, it's value will be returned, otherwise the default value will. This is the effectively the same as js var foo='bar'; return (foo ? foo : 'default');

I could be off, but from the behavior I've seen so far, the order does need to work so that the right-hand side is the variable side because if both sides are true, the right-hand side is returned.

Approach #2 If you need more control such as when the values you want to return and the values you need to evaluate aren't the same, then you might do: @defaultValue[not(true_condition)] | @ValueYouNeed[true_condition] combine that with the exslt:node-set() trick to have a string value for the default rather than another node evaluation.

Paul Wagland
  • 27,756
  • 10
  • 52
  • 74
rainabba
  • 3,804
  • 35
  • 35
0

This is possible using fn:sum(...), which enables you a standard XPath 2.0 query. It sums up all numbers in a sequence, if it is empty, it returns 0. All you've got to do is add all elements which are a number, in your case without attribute @type='null'.

sum(
  (
    //ea[not(@type eq 'null')],
    //pa[not(@type eq 'null')],
    //perf[not(@type eq 'null')]
  )
)
Jens Erat
  • 37,523
  • 16
  • 80
  • 96
  • This isn't a syntactically correct XSLT 1.0 transformation, but the OP has tagged the question as "xslt-1.0" – Dimitre Novatchev Apr 10 '13 at 02:29
  • He's looking for a solution in either XPath or XSLT: the question is "does there exist a ternary operation in XPath/XSLT[...]?" which is answered by this question. It is tagged "Xpath", too, by the way. As swshaun not even provided a valid XML context with these three lines, I am pretty sure he's able to execute that snippet on the context he needs anyway. – Jens Erat Apr 10 '13 at 08:22
  • Jens Erat, The version of XPath that is available from XSLT 1.0 is XPath 1.0. Your answer is completely inadequate to this question and most likely the OP hasn't yet tried to run your code. And why are you talking about XPath 1.0 in your answer at all? This is doubly misleading. – Dimitre Novatchev Apr 10 '13 at 14:39
  • I have decided to do a two stage transform -- one to cleanse the data I have (which I have no control over) and reform it into something meaningful which can be operated over. The fundamental question has been answered though... There is no ternary operator. – swshaun Apr 11 '13 at 10:53
0

Apologies if this comes up twice, there were problems posting earlier on today!!

From the above, it seems fair to say there does not exist a ternary operator for evalutaing expressions, so I have done a two stage transform instead to, once to populate empty elements and then one to perform the operations that I want.

Thank you for your answers.

swshaun
  • 384
  • 1
  • 4
  • 12