Using Variables Into XPath Queries In ColdFusion
In ColdFusion 10, xmlSearch() added support for XPath 2.0 syntax and passing variables into XPath queries. Using variables can make the query more readable; but, since XML is case-sensitive, it took me a few minutes to get it working. As such, I figured I'd put together a quick demo on using variables in XPath queries.
When using variables in XPath queries, you reference the variable name by prefixing it with a "$". You can then pass a struct of name-value pairs into xmlSearch() as an optional 3rd argument. But, you have to be careful in how you define that struct. My first attempt looked something like this:
<cfscript>
// ...
bestFriends = xmlSearch(
friends,
"//friend[ id/text() = $id ]",
{
id = 4
}
);
// ...
</cfscript>
Notice that I'm passing in "id" and then referencing it as "$id" in the XPath query. Unfortunately, when I ran this code, I got the following ColdFusion error:
Unable to process the result of the XMLSearch for Undeclared variable in XPath expression: $id. ColdFusion is unable to process the result of the XPath search. You may have an undefined variable in the xpath expression.
After some head-scratching, I realized that it was a case-sensitivity issue. XML is case-sensitive, ColdFusion is not. And, what I forgot would happen behind the scenes is that ColdFusion would silently upper-case my struct keys (making the input case different from the reference case). To fix this, I had to quote the variable names when defining the input struct:
<!--- Define our XML document object. --->
<cfxml variable="friends">
<friends>
<friend>
<id>1</id>
<name>Joanna</name>
</friend>
<friend>
<id>2</id>
<name>Tricia</name>
</friend>
<friend>
<id>3</id>
<name>Sarah</name>
</friend>
<friend>
<id>4</id>
<name>Kim</name>
</friend>
</friends>
</cfxml>
<cfscript>
// When passing in variables, make sure to !!quote!! the names. XML is case-sensitive
// and it's important that you explicitly define the case of the variables using quotes;
// otherwise, ColdFusion may upper-case them, making unavailable in the XPath execution.
bestFriends = xmlSearch(
friends,
"//friend[ ( id/text() = $id ) or contains( $names, name/text() ) ]",
{
"id" = 4,
"names" = "Sarah,Jane,Annie"
}
);
// Collect the names and output the list of resultant friends.
names = [];
for ( friend in bestFriends ) {
arrayAppend( names, friend.name[ 1 ].xmlText );
}
writeOutput( "Besties: " & arrayToList( names, ", " ) );
</cfscript>
As you can see, this time, my variable names are quoted. And, when I ran this code, I was able to successfully locate my XML nodes:
Besties: Sarah, Kim
This is a small feature, but a rather nice one. I don't deal with XML much these days; but, it's good to have these tidbits packed away, in the back of my mind, for the occasions that call for some sweet extensible markup language.
Want to use code from this post? Check out the license.
Reader Comments
@All,
This is a classic move; I just found an old post that talks about some of the XPath 2.0 updates including... who would have guessed... but an example on how to use variables in xmlSearch():
www.bennadel.com/blog/2340-coldfusion-10-xmlsearch-and-xmltransform-now-support-xpath-2-0.htm
Oh well, good refresher anyway.