Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion

Posted March 10, 2011 at 10:49 AM by Ben Nadel

Tags: ColdFusion

Years ago, in my introduction to xmlSearch() and XPath in ColdFusion, I talked about the / and // expressions. As far as describing node locations, both of these expressions have a different meaning depending on where they are located within the greater XPath value. Up until now, however, I had never thought too deeply about the use of these expressions in an xmlSearch() call that didn't originate at the root XML element. As is turns out, sub-tree searching has few interesting expression-based caveats.

Before we get into the sub-tree searching, let's just quickly recap how / and // work in relation to the root node. When / is used at the beginning of a path:

/a

... it will define an absolute path to node "a" relative to the root. As such, in this case, it will only find "a" nodes at the root of the XML tree.

When // is used at the beginning of a path:

//a

... it will define a path to node "a" anywhere within the XML document. As such, in this case, it will find "a" nodes located at any depth within the XML tree.

These XPath expressions can also be used in the middle of an XPath value to define ancestor-descendant relationships. When / is used in the middle of a path:

/a/b

... it will define a path to node "b" that is a direct descendant (ie. a child) of node "a".

When // used in the middle of a path:

/a//b

... it will define a path to node "b" that is any descendant of of node "a".

Ok, so that brings us back up to speed with how / and // expressions can be used with root-relative XPath values; but, what about when we have an existing reference to a sub-node of an XML tree? How do these expressions work?

As it turns out, they work exactly the same - but, in order to define a "relative" path you have to use dot-notation. ColdFusion's xmlSearch() function takes a node and an XPath value. That XPath value adheres to the same rules thats we discussed above. And, if you want to use the "in the middle of a path" concept, you have to start your XPath value with a "." (period).

So, given some sub-node, "x", the XPath:

//y

... will still find any node, "y", located anywhere within the XML tree. But, the XPath:

.//y

... will find any node, "y", that is a descendant of the node "x." In other words, preceding the "//" expression with a "." tells the XML search engine to execute the search relative to the current node reference.

To illustrate this concept, take a look the following code. In it, we're going to grab a sub-node reference and then perform a few searches using "//" and ".//" notation.

  • <!---
  • Define our XML node tree. Notice that this is going to have
  • a bunch of nested nodes of the same type; this way, we can see
  • how relative pathing will work.
  • --->
  • <cfxml variable="data">
  •  
  • <nodes id="1" level="root">
  •  
  • <nodes id="2" level="1">
  •  
  • <nodes id="3" level="2">
  •  
  • <nodes id="4" level="3">
  •  
  • <node />
  •  
  • </nodes>
  •  
  • <nodes id="5" level="3">
  •  
  • <node />
  •  
  • </nodes>
  •  
  • </nodes>
  •  
  • </nodes>
  •  
  • <nodes id="6" level="1">
  •  
  • <nodes id="7" level="2">
  •  
  • <nodes id="8" level="3">
  •  
  • <node />
  •  
  • </nodes>
  •  
  • <nodes id="9" level="3">
  •  
  • <node />
  •  
  • </nodes>
  •  
  • </nodes>
  •  
  • </nodes>
  •  
  • </nodes>
  •  
  • </cfxml>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Now, get a reference to the first level-2 nodes
  • element. Our subsequent searches will be performed
  • in relation to that one.
  •  
  • NOTE: This is the node with ID: 3.
  • --->
  • <cfset searchNode = data.nodes[ 1 ].nodes[ 1 ].nodes[ 1 ] />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Now that we have a base node for searching, we are going to
  • search for all NODES using both a dot-relative and
  • non-dot-relative approaches.
  • --->
  •  
  • <!--- The first uses tree-wide location -- //. --->
  • <cfset nodes = xmlSearch( searchNode, "//nodes" ) />
  •  
  • <!--- Output the results. --->
  • <cfdump
  • var="#nodes#"
  • label="Using // Notation"
  • />
  •  
  •  
  • <br />
  •  
  •  
  • <!--- The first uses LOCAL tree-wide location -- .//. --->
  • <cfset nodes = xmlSearch( searchNode, ".//nodes" ) />
  •  
  • <!--- Output the results. --->
  • <cfdump
  • var="#nodes#"
  • label="Using .// Notation"
  • />
  •  
  •  
  • <br />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • And, just as a sanity check, we'll use a double-slash a
  • sub-path of a greater XPath value. This should be the same
  • thing as the local-relative pathing.
  • --->
  • <cfset nodes = xmlSearch(
  • data,
  • "/nodes[ 1 ]/nodes[ 1 ]/nodes[ 1 ] // nodes"
  • ) />
  •  
  • <!--- Output the results. --->
  • <cfdump
  • var="#nodes#"
  • label="Using // In-Path"
  • />

When we run the above code, comparing the use of "//" and ".//" XPath expressions in node-relative xmlSearch() calls, we get the following output:

 
 
 
 
 
 
Using / and // and .// in XPath XML search expressions in ColdFusion. 
 
 
 

As you can see, when performing a node-relative xmlSearch() call in ColdFusion, you have to prefix the XPath value with "." to make the expression "//" location-relative; otherwise, the given XPath value will operate as if you were searching from the root node.




Reader Comments

Mar 10, 2011 at 11:33 AM // reply »
50 Comments

That is excellent! I *just* ran into a need for that very syntax.


Mar 10, 2011 at 11:36 AM // reply »
11,241 Comments

@Steve,

Awesome then - perfect timing :)


Mar 10, 2011 at 1:01 PM // reply »
35 Comments

Somewhat reminiscent of *NIX, with . representing the current [folder/node].

Very useful to know. Even though you can set up a schema so that a certain node can always be found in a certain place in a document (so that absolute XPath references seem to be appropriate), it's probably better to look for a node based on where the business requirement says it should be rather than where the current schema says it could be found. Someone coming along behind you may need to update the schema ...


Mar 10, 2011 at 1:57 PM // reply »
11,241 Comments

@Dave,

Yeah, very much like that. You can use "." for current node and ".." to get to the parent node, very much like a file path.

I've heard of the schema being used to set variables (I believe Adam Tuttle mentioned it once in a comment for high-ascii values or something). I have that concept filed away for further investigation.


Mar 10, 2011 at 2:17 PM // reply »
35 Comments

@Ben,

I was thinking of it from a more abstract perspective. For example, you might have an XML document that contains NCAA teams, with a top-level level* element, child conference elements, and child team elements. (So you could find a team with /level/conference/team.)

You want to display all teams within a conference, so if you are looking at a conference node, it's tempting to look for ./team to get all teams in the conference.

However, that only works as long as team is a direct child of conference. If the conference expands to include divisions, and teams are now children of divisions (/level/conference/division/team), ./team doesn't work at the conference level, but .//team does.

*For those familiar with NCAA sports, yes, I know the top level in the hierarchy is Division, but then we may run into problems distinguishing Division (I/II/III) from Division (East/West).


Apr 20, 2012 at 12:04 PM // reply »
1 Comments

Excellent...

You just gave me, exactly what I have been looking for...

I do have 1 more questions as well...

What is the meaning of ".." on xPath?

Does /a/../z this equals to below path(s)?

1. /a/b/z [or] /a/c/z [or] /a/d/z ?
2. /a/b/c/d/z [and] /a/b/c/d/e/f/z ?


Jun 11, 2012 at 7:29 AM // reply »
1 Comments

Thanks for the tips, i'm using XPath in Java, but it works the same way.
Really clear and helpful!


Jan 10, 2013 at 10:52 AM // reply »
2 Comments

Thanks Ben, another useful post -- I was scratching my head wondering why subqueries "weren't working".


Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
May 22, 2013 at 4:43 AM
How Do You Use The ColdFusion CFParam Tag?
'<cfparam>' or 'isDefined()and <cfset>' performs the same task.Is there any difference? ... read »
May 21, 2013 at 7:46 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
No luck. At least I have uncovered the cause, URLScan 3.1. Here is what I see in the IIS log when a file is over 30mb. 2013-05-21 23:29:05 10.105.45.128 GET /plupload/assets/jquery/jquery-1.8. ... read »
May 21, 2013 at 6:12 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
Ben, I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when ... read »
May 21, 2013 at 11:51 AM
Ask Ben: Parsing Very Large XML Documents In ColdFusion
Looking at my first ever XML document that I have to parse and put into MS SQL 2000 with CF8. I get it to list the desired Field name, many times over, and have a long list of this field name displa ... read »
May 21, 2013 at 9:25 AM
Turning Off and On Identity Column in SQL Server
you are awesome..i am lucky to get this blog between such a garbage one....Thanks, Prashant ... read »
May 20, 2013 at 4:38 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, Your confusion is well founded, since this is a very confusing features. In fact, it ONLY works if you use array notation. Meaning, that this: arrayToList( query[ "columnName" ] ) ... read »
May 20, 2013 at 4:34 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I was thinking chicken and the egg, I wouldn't have expected it to work in the valuelist going in I guess. Maybe I just need a beer, long day :) ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools