16

If I have the below XML, how to specify a xpath to return a string based on a condition. For example here if //b[@id=23] then "Profit" else "Loss"

<a>
  <b id="23"/>
  <c></c>
  <d></d>
  <e>
    <f id="23">
       <i>123</i>
       <j>234</j>
    <f>
    <f id="24">
       <i>345</i>
       <j>456</j>
    <f>
    <f id="25">
       <i>678</i>
       <j>567</j>
    <f>
  </e>
</a>
Sivaraman L
  • 175
  • 1
  • 1
  • 5

2 Answers2

23

I. XPath 2.0 solution (recommended if you have access to an XPath 2.0 engine)

   (: XPath 2.0 has if ... then ... else ... :) 

   if(//b[@id=23]) 
     then 'Profit' 
     else 'Loss'

II. XPath 1.0 solution:

Use:

concat(substring('Profit', 1 div boolean(//b[@id=23])),
       substring('Loss', 1 div not(//b[@id=23]))
      )

Verification using XSLT 1.0:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:value-of select=
   "concat(substring('Profit', 1 div boolean(//b[@id=23])),
           substring('Loss', 1 div not(//b[@id=23]))
          )"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document (corrected to make it well-formed):

<a>
    <b id="23"/>
    <c></c>
    <d></d>
    <e>
        <f id="23">
            <i>123</i>
            <j>234</j>
        </f>
        <f id="24">
            <i>345</i>
            <j>456</j>
        </f>
        <f id="25">
            <i>678</i>
            <j>567</j>
        </f>
    </e>
</a>

produces the wanted, correct result:

Profit

When we replace in the XML document:

<b id="23"/>

with:

<b id="24"/>

again the correct result is produced:

Loss

Explanation:

We use the fact that:

substring($someString, $N)

is the empty string for all $N > string-length($someString).

Also, the number Infinity is the only number greater than the string-length of any string.

Finally:

number(true()) is 1 by definition,

number(false()) is 0 by definition.

Therefore:

1 div $someCondition

is 1 exactly when the $someCondition is true()

and is Infinity exactly when $someCondition is false()

Thus it follows from this that if we want to produce $stringX when $Cond is true() and to produce $stringY when $Cond is false(), one way to express this is by:

concat(substring($stringX, 1 div $cond),
       substring($stringY, 1 div not($cond)),
      )

In the above expression exactly one of the two arguments of the concat() function is non-empty.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • 2
    +1 Dimitre you always provide outstanding explanations, especially to questions involving XPath. I speak from experience :) – Dan Lugg Aug 13 '11 at 05:12
  • 1
    I'm using a variant of the XPath 1.0 solution and it is working great. Many thanks. concat(substring($stringX, 1 div $cond), substring($stringY, 1 div $otherCond)), ) – David Welborn Oct 11 '17 at 22:05
  • @DanLugg that's a nice thing to say Dan. Just speaking as a bystander. Keep it up :) – Tails Aug 28 '21 at 20:57
0

You can't; you'd have to use XQuery for this. see e.g. XQuery Conditional Expressions

Or, if the resulting string is only used within Java, you can just process the value returned by XPath within your Java code:

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//b[@id=23]");
boolean result = expr.evaluate(doc, XPathConstants.BOOLEAN);

if (result) return "Profit";
else return "Loss";
michel-slm
  • 9,438
  • 3
  • 32
  • 31
  • I am using JAXP for xml processing. How to use this xquery in this? – Sivaraman L Aug 12 '11 at 19:22
  • If you're using JAXP, then why not just test for the retrieved string value on the Java side? – michel-slm Aug 12 '11 at 19:24
  • My issue is based on the node attribute value I have to decide on the result string. When I tried to retrieve the string nothing comes in as this node just has one attribute . If I give the conditon like //b[@id=23] it is returning a boolen value. – Sivaraman L Aug 12 '11 at 19:27
  • Can't you check if that boolean value is true, and return the appropriate string based on that? e.g. if (node_has_attribute) return "Profit" else return "Loss"; – michel-slm Aug 12 '11 at 19:31
  • yes I checked and the value returned is true, but I don't know how to return "profit" for true. Any sample code would really help me. – Sivaraman L Aug 12 '11 at 19:34
  • see added example code -- it really is just what I said before; just take the boolean value from evaluating the XPath expression, and test it with a Java 'if' and return / assign the proper string there. – michel-slm Aug 12 '11 at 19:44