I saw a recent post that expressed concern that a XMLSearch bug in ColdFusion MX 6/7 still has not been fixed in ColdFusion MX 7.01. I think its just a misunderstanding about how to reference namespaces, specifically the no-name namespace, and in fact there is no such bug.

To back up, I learned about this special case from a blog entry by Pete Freitag where Sean Corfield proposed the solution in the comments, so my blog entry here is just a reiteration of that solution.

To begin, consider the following XML Document, test.xml:

view plain print about
2 xmlns="http://ns.r-xml.org/2004-08-02"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 userId="TMTest01">

5<!-- Example of Search Status results -->
6 <ProviderReferenceId>
7     <IdValue>204</IdValue>
8 </ProviderReferenceId>

Notice how two namespaces are defined:

  • xmlns="http://ns.r-xml.org/2004-08-02"
  • xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

Both of these namespaces are defined in the root element, BackgroundReports. Which namespace does the node BackgroundReports belong to? What namespace does IdValue belong to?

Now look at the following code:
view plain print about
1[cfset currentDir = getDirectoryFromPath(ExpandPath('*.*'))>
2[cffile action="read" file="#currentDir#/test.xml" variable="unparsedXmlDoc">
3[cfset myXmlDoc = XMLParse(unparsedXmlDoc) />
4[cfset myResults = XMLSearch(myXmlDoc,"/BackgroundReports/ProviderReferenceId/IdValue") />
5[cfdump var="#myResults#">

And the output of the myResult search result from CFDUMP:
view plain print about

The result of this is that the variable myResult contains an empty array object. The XPath Expression /BackgroundReports/ProviderReferenceId/IdValue did not match its intended target. This XPath expression qualifies the BackgroundReport node and its children as having no namespace.

However, the BackgroundReport node is in a namespace, the no-name namespace. Look again at just the root element. I've isolatd just the important part for clarity:
view plain print about
1<BackgroundReports xmlns="http://ns.r-xml.org/2004-08-02" ....>

Notice how the xmlns="blah blah" namespace definition is lacking a namespace qualifier compared to this one which has a namespace qualifier foo, xmlns:foo="blah blah". When you define a namespace without a prefix qualifier, the element in which the namespace is defined belongs to that namespace unless you prefix that element. Got that? :)

The code example above failed because the XPath expression assumed that the target node was in no namespace at all, but instead it is in the no-name namespace. So how should the XPath expression be written to indicate the elements in the node are in the no-name namespace? Use just an empty colon, ":" .
view plain print about

Here we are saying that we are searching for all elements IdValue that are in the noname namepsace by refering to it as :IdValue. But we have to declare the namespace of its parents namespaces too, so we end up with the expression /:BackgroundReports/:ProviderReferenceId/:IdValue.

Rewriting the code to use the improved XPath expression, we have the following:

view plain print about
1[cfset currentDir = getDirectoryFromPath(ExpandPath('*.*'))>
2[cffile action="read" file="#currentDir#/fekke.xml" variable="unparsedXmlDoc">
3[cfset myXmlDoc = XMLParse(unparsedXmlDoc) />
4[cfset myResults = XMLSearch(myXmlDoc,"/:BackgroundReports/:ProviderReferenceId/:IdValue") />
5[cfdump var="#myResults#">

This produces the hoped for result which is the myResult variable populated with the matching IdValue node, which looks like this in CFDUMP:

view plain print about
2[1][xml element]

This was bug 59667 for CFMX 6.1, which I've recommended to be closed. I've added a comment to the XMLSearch LiveDoc and will propose a technote.