Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Adam Lewis
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Adam Lewis

Using Variables Into XPath Queries In ColdFusion

By Ben Nadel on
Tags: 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.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.