Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested conditional if else statements in xpath

I have this XML:

<property id="1011">
    <leasehold>No</leasehold>
    <freehold>Yes</freehold>
    <propertyTypes>
        <propertyType>RESIDENTIAL</propertyType>
    </propertyTypes>
</property>

and I want to create an xpath statement that is same as the following nested if-else pseudocode block.

if( propertyTypes/propertyType == 'RESIDENTIAL') {
    if( leasehold == 'Yes' ){
        return 'Rent'
    } else
        return 'Buy'
    }
} else {
    if( leasehold == 'Yes' ){
        return 'Leasehold'
    } else
        return 'Freehold'
    }
}

I've seen something about Becker's method but I couldn't really follow it. XPath isn't my strong point really.

like image 593
Kirk WSI Avatar asked Oct 19 '12 15:10

Kirk WSI


People also ask

How to use if in XPath?

An XPath expression beginning with the keyword 'if' signifies a conditional expression. if (/company/office[@location = 'Boston']/employee[1]/age = 35) then 'is 35' else 'is not 35'. The keyword 'if' is followed by a test expression in parentheses which returns a boolean value i.e. 'true' or 'false'.

How do you write conditional XPath expression?

To define an XPath expression that checks if a string element is empty, you must use the operator != . This example shows how to define an XPath expression that evaluates to true when a repeating element, which is referred to as a sequence, is empty. The effective Boolean value of an empty sequence is false.

How do you check if an element exists in the XML using XPath?

Use the fn:nilled XPath function to test whether the value of an input element has the xsi:nil attribute set. Use the fn:exists XPath function to test whether the value of an input element is present. Note: An XML element that has the xsi:nil attribute set is considered to be present.


3 Answers

I. In XPath 2.0 one simply translates this to:

   if(/*/propertyTypes/propertyType = 'RESIDENTIAL')
    then
     (if(/*/leasehold='Yes')
       then 'Rent'
       else 'Buy'
     )
     else
       if(/*/leasehold='Yes')
         then 'Leasehold'
         else 'Freehold'

XSLT 2.0 - based verification:

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

 <xsl:template match="/">
  <xsl:sequence select=
   "if(/*/propertyTypes/propertyType = 'RESIDENTIAL')
   then
     (if(/*/leasehold='Yes')
       then 'Rent'
       else 'Buy'
     )
     else
       if(/*/leasehold='Yes')
         then 'Leasehold'
         else 'Freehold'
   "/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<property id="1011">
    <leasehold>No</leasehold>
    <freehold>Yes</freehold>
    <propertyTypes>
        <propertyType>RESIDENTIAL</propertyType>
    </propertyTypes>
</property>

the XPath expression is evaluated and the result of this evaluation is copied to the output:

Buy

II. XPath 1.0 solution

In XPath 1.0 there isn't an if operator.

A conditional statement can still be implemented with a single XPath 1.0 expression, but this is more tricky and the expression may not be too readable and understandable.

Here is a generic way (first proposed by Jeni Tennison) to produce $stringA when a condition $cond is true() and otherwise produce $stringB:

concat(substring($stringA, 1 div $cond), substring($stringB, 1 div not($cond)))

One of the main achivements of this formula is that it works for strings of any length and no lengths need to be specified.

Explanation:

Here we use the fact that by definition:

number(true()) = 1

and

number(false()) = 0

and that

1 div 0 = Infinity

So, if $cond is false, the first argument of concat() above is:

 substring($stringA, Infinity)

and this is the empty string, because $stringA has a finite length.

On the other side, if $cond is true() then the first argument of concat() above is:

sibstring($stringA, 1) 

that is just $stringA.

So, depending on the value of $cond only one of the two arguments of concat() above is a nonempty string (respectively $stringA or $stringB).

Applying this generic formula to the specific question, we can translate the first half of the big conditional expression into:

concat(
           substring('rent',
                      1 div boolean(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      ),
           substring('buy',
                      1 div not(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      )
               )

This should give you an idea how to translate the whole conditional expression into a single XPath 1.0 expression.

XSLT 1.0 - based verification:

<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:copy-of select=
   "concat(
           substring('rent',
                      1 div boolean(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      ),
           substring('buy',
                      1 div not(/*[leasehold='Yes'
                                     and
                                       propertyTypes/propertyType = 'RESIDENTIAL'
                                      ]
                                  )
                      )
               )
   "/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document (above), the XPath expression is evaluated and the result of this evaluation is copied to the output:

buy

Do note:

If you decide to replace the specific strings with other strings that have different lengths than the original, you simply replace these strings in the above XPath 1.0 expression and you don't have to worry about specifying any lengths.

like image 166
Dimitre Novatchev Avatar answered Oct 01 '22 23:10

Dimitre Novatchev


Becker's method for your data is the following:

concat(substring('Rent',      1 div boolean(propertyTypes/propertyType ="RESIDENTIAL" and leasehold="Yes")),
       substring('Buy',       1 div boolean(propertyTypes/propertyType ="RESIDENTIAL" and leasehold="No")),
       substring('Leasehold', 1 div boolean(propertyTypes/propertyType!="RESIDENTIAL" and leasehold="Yes")),
       substring('Freehold',  1 div boolean(propertyTypes/propertyType!="RESIDENTIAL" and leasehold="No")))
like image 29
choroba Avatar answered Sep 30 '22 23:09

choroba


Spent all day today, but works for me this is for Xpath 1.0:

concat(
substring(properties/property[@name="Headline"], 1, string-length(properties/property[@name="Headline"]) * 1), 
substring(properties/property[@name="Name"], 1, not(number(string-length(properties/property[@name="Headline"]))) * string-length(properties/property[@name="Name"]))
)
like image 36
Pavel Philippenko Avatar answered Oct 02 '22 23:10

Pavel Philippenko