Using Multiple Predicates In The Same Part Of An XPath Query In ColdFusion
The other day, Will Belden asked me a question about XPath expressions in ColdFusion. Specifically, how to select an XML node based on the existence of multiple child nodes. This seemed like a problem that could be solved using multiple predicates in the same part of the XPath query; but, in the past, I think I've only ever used a single predicate. As such, I wanted to put together a quick demo to make sure that this approach worked as expected.
In XPath, predicates are used to locate a specific node (ex. the 3rd node in a collection) or to locate a node that contains another value (ex. contains a child node called, "ISBN"). A single predicate can contain multiple conditions (using "and" and "or"); but, can that compound expression be broken up into several, individual predicates?
To see this in action, I'm going to find all my friends that love both Movies and Food:
<!--- Build Friends XML document object. --->
<cfxml variable="friends">
<friends>
<friend>
<id>1</id>
<name>Tricia</name>
<loves>Movies</loves>
<loves>Food</loves>
<loves>Gym</loves>
</friend>
<friend>
<id>2</id>
<name>Sarah</name>
<loves>Food</loves>
<loves>Reading</loves>
</friend>
<friend>
<id>3</id>
<name>Kim</name>
<loves>Movies</loves>
<loves>Art</loves>
<loves>Food</loves>
</friend>
</friends>
</cfxml>
<!---
Get friends who love food and movies.
--
NOTE: This time, we are using an XPath query that defined two predicates on the
same point in the path.
--->
<cfset bestFriends = xmlSearch(
friends,
"//friend[ loves/text() = 'Food' ][ loves/text() = 'Movies' ]/name/text()"
) />
<cfdump
var="#bestFriends#"
label="[ loves/text() = 'Food' ][ loves/text() = 'Movies' ]"
/>
<br />
<!---
Get friends who love food and movies.
--
NOTE: This time, we are using a single predicate that contains multiple conditions.
--->
<cfset bestFriends = xmlSearch(
friends,
"//friend[ ( loves/text() = 'Food' ) and ( loves/text() = 'Movies' ) ]/name/text()"
) />
<cfdump
var="#bestFriends#"
label="[ ( loves/text() = 'Food' ) and ( loves/text() = 'Movies' ) ]"
/>
Notice that I am trying to execute this search twice - once using multiple predicates and once using a single predicate with multiple conditions. And, when we run this code we get the following page output:
As you can see, both approaches worked and were able to locate the friends that loved movies and food. It seems like the difference might come down to one of personal preference. I find multiple predicates easier to read; but, I also feel like the compound expression might express its intent a bit more clearly. I could go either way on this one.
Want to use code from this post? Check out the license.
Reader Comments
Hi,
I need Xpath, which will run "n" number of times based on the condition.
for Eg: /a:Charge[//a:IsSSR="true"]/a:Amount
The avobe mentioned will return the Amount where the IsSSR value is true .
But i have multiple tags where IsSSR is true.
so i need to retrieve all the values where IsSSR is true.
Can you please help me out
@Amith,
Do you mean you need to find any tag that has the attribute "IsSSR=true", regardless of the tag name?
Actually "isssr" is also one tag in my xml.
I need to find this tag wher ever it is in my xml. Usually we wil have this tag in multiple repetitions. and amount is the other tag which will be with this tag.
So I need to retieve amount tag value wher ever the tag issr is equals to true