Monday, June 3, 2013

Using XQuery to get values from set of XPaths and return null if node is not presented.

I have been stuck for few days due to that i can not return anything from querying XPath if that specific XPath is not presented in my XML file. The scenario is that we have nodes in our XML file that may be or may not be present in XML file arbitrarily.

Yes you can do it programmatically if you only use one XPath at a time. But my need is different. I want to get values of several XPaths at once and at the same time i need it to give me something pre-defined ( let's say "null"), if one of XPaths i am querying for is not presented.

So i basically had two problems to overcome. One is how to get values of several XPaths at once.
And the other is getting per-defined value if that specific node is not presented in my XML document.

First i tried using purely XPaths. In this way i have been able to overcome my first problem, that is querying several XPaths at once. That is by using XPath Union operation. XPath union operator can be use as follows

 /A/B/C/text() | /A/D/C/text() | /A/E/C/text()  

or

 /A/B/C/text() union /A/D/C/text() union /A/E/C/text()  

Here by using "|"  or "union" you  can join as much as XPaths you want. And i have seen that this improves performance since you can use to get several values you want through out the system at one call without calling the same function (which you use to execute your XPaths) several times.

Also order of the values return by using XPath union preserve the order of the elements appear in XML document.

Problem in this method come with this order. If you join an element that will not present in the XML file always, it will destroy the order of the values return. Which is lets say if all the "C" elements present in above example, then the returning order values should be,

 1. value of /A/B/C/text()   
 2. value of /A/D/C/text()   
 3. value of /A/E/C/text()   

But, lets say that Node /A/D/C is not presented in XML file.Then the order will be,

 1. value of /A/B/C/text()   
 2. value of /A/E/C/text()   

Yes you get it right!!! Nothing in the middle saying that we have no /A/D/C node. So the order of the return values have been changed.

Because of this I have find an alternative and XQuery came to rescue. Yes XPath is to do complicate stuff like querying and modifying results. That is the job of XQuery. XPath is there to show the way to XQuery ("hey dude the node you are looking for is this way")

So by using XQuery FLOWER statement i have been able to write following query to evaluate set of XPaths and get their values at the same time and also if there is some node missing in the middle to return "null" which help to preserve the order of the return values.

 let $i1:=/A/B/C/text()   
 let $i2:=/A/D/C/text()   
 let $i2:=/A/E/C/text()  
 return string-join((if($i1) then ($i1) else ('null') , if($i2) then ($i2) else ('null') ) , ';')  

Here i have used string-join(string1, string2 ... stringN, 'delemeter') function of XQuery to join the results and ';' as the delimiter.

We can use if(condition or XPath) then (statement or XPath) else (statement or XPath)to check the availability of node and send a per-defined value if node is missing.