Steven Erat's Blog
 
 
Viewing By Entry
 
 

TalkingTree  XMLSearch() function works when qualifying a noname namespace

 

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:

<BackgroundReports
xmlns="http://ns.r-xml.org/2004-08-02"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
userId="TMTest01">

<!-- Example of Search Status results -->
<ProviderReferenceId>
    <IdValue>204</IdValue>
</ProviderReferenceId>
</BackgroundReports>
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:

<cfset currentDir = getDirectoryFromPath(ExpandPath('*.*'))>
<cffile action="read" file="#currentDir#/test.xml" variable="unparsedXmlDoc">
<cfset myXmlDoc = XMLParse(unparsedXmlDoc) />
<cfset myResults = XMLSearch(myXmlDoc,"/BackgroundReports/ProviderReferenceId/IdValue") />
<cfdump var="#myResults#">

And the output of the myResult search result from CFDUMP:

[array][empty]

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:

<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, ":" .

/:BackgroundReports/:ProviderReferenceId/:IdValue

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:

<cfset currentDir = getDirectoryFromPath(ExpandPath('*.*'))>
<cffile action="read" file="#currentDir#/fekke.xml" variable="unparsedXmlDoc">
<cfset myXmlDoc = XMLParse(unparsedXmlDoc) />
<cfset myResults = XMLSearch(myXmlDoc,"/:BackgroundReports/:ProviderReferenceId/:IdValue") />
<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:

[array]
[1][xml element]
[Xmlname][IdValue]
[XmlNsPrefix][]
[XmlNsURI][http://ns.r-xml.org/2004-08-02]
[XmlText][204]
[XmlComment][]
[XmlAttributes][struct[empty]]
[XmlChildren][]

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.

 


Comments

Thanks Sean. I added a Livedocs comment,and I'll propose a technote as well. By the way, had to delete your comment since my comment pod couldn't (yet) handle a long, nonbreaking string.


Don't forget that you can drop the cffile read if you want, and just add the filepath in xmlparse()

ColdFusion MX 7:

* Added the validator parameter.
* Added support for filenames and URLs in the xmlText parameter.

Will


Steven,

Thank you for the explanation. I enetered this as a bug, and was told that it would be fixed for the next point release. I will add a blog post with a trackback to your blog for the explanation.

David Fekke


I just hit an interesting one with this XML code, trying to retrieve the userid tag (this is Blogger's API). I can't figure out how to reference the userid:

[feed xmlns="http://purl.org/atom/ns#"]
[userid xmlns="http://www.blogger.com/atom/ns#"]24250907[/userid]
[link href="https://www.blogger.com/atom/28011183" rel="service.post" title="Test Automated Blog" type="application/atom+xml"/]
[link href="https://www.blogger.com/atom/28011183" rel="service.feed" title="Test Automated Blog" type="application/atom+xml"/]
[link href="http://seancorfield.blogspot.com" rel="alternate" title="Test Automated Blog" type="text/html"/]
[/feed]


I've been working on it for an hour now, but can't find a solution. There are two no-name namespaces in the example. The 'feed' element is in the first (default) no-name namespace, and so are its 'link' children. The feed node as a whole is returned by the XPath expression /:feed, and the link children are accessible by /:feed/:link, but since userid declares another no-name namespace the userid element (and only userid) becomes part of that second no-name namespace and cannot be accessed through the first no-name namespace. This causes /:feed/:userid to fail.

Removing the second no-name namespace or giving a prefix to the second namespace makes /:feed/:userid work.

There must be a special syntax to distinguish nested no-name namespaces.


I spent quite a bit of time trying to make it work before I decided to share my pain with you! :)

If anyone can figure this out, I'd love to know!


Have you tried to not use the no-name namespace completely on the XMl elements that are already a no-name namespace?

Maybe I am not following...


Sean, this worked for me:

[cfset userid = xmlSearch(xmlPacket, "/:feed/*[starts-with(name(),'userid')]")]

[cfdump var="#userid#"]


You may have already tried this, but this also works as far as converting everything to an array:

xmlSearch(xmlPacket, "/:feed/*")

It's not a clean or perfect, but you could loop over the array until you find the "userid" element. I still have not found a way to directly access the "userid" element without some sort of wildcard.


D'oh! I made my first example one function-reference too complex. This works the same without the "starts-with" function:

xmlSearch(xmlPacket, "/:feed/*[name()='userid']")


Michael,

Thanks very much for the XPath help. This works perfectly to pluck out the userid field from the feed node.


Not sure if you guys can enlighten me...

I'm running into a similar issue and cant seem to get it resolved,no matter what I try. I thought since you provided insight earlier I mail you.

I have the following XML and want to retrieve the key through a search.

[?xml version="1.0" encoding="UTF-8"?]
[soapenv:Envelope
   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance"]
   [soapenv:Body]
      [postExternalEventResponse
         xmlns="urn:sungardhe:workflow:ws:messages:1.1"]
         [externalEventPK]
            [key]199555[/key]
         [/externalEventPK]
      [/postExternalEventResponse]
   [/soapenv:Body]
[/soapenv:Envelope]



I tried various variations you suggest, somthing like:

[cfset arWork = XMLSearch(xmlResponse, "/:postExternalEventResponse")]
         [cfdump var="#arWork#"]

Won't work.

Do you have any tips?


If the nested namespace is of no importance to you when parsing the xml payload for specific information, then you could use a regex to find and remove it before searching:

[!--- start with XML document as a string ---]
[cfsavecontent variable="xmldoc"][?xml version="1.0" encoding="UTF-8"?]
[soapenv:Envelope
   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance"]
   [soapenv:Body]
      [postExternalEventResponse
         xmlns="urn:sungardhe:workflow:ws:messages:1.1"]
         [externalEventPK]
            [key]199555[/key]
         [/externalEventPK]
      [/postExternalEventResponse]
   [/soapenv:Body]
[/soapenv:Envelope]
[/cfsavecontent]

[!--- eliminate the troublesome no-name namespace on the first child element ---]
[cfset pattern = 'xmlns="urn[:\.\w\n]*"']
[cfset xmldoc = rereplace(xmldoc,pattern,'','one')]

[!--- print out the xmldoc just to check ---]
[pre][xmp][cfoutput]#xmldoc#[/cfoutput][/xmp][/pre]

[!--- convert string to xml object ---]
[cfset xmlobj = xmlparse(xmldoc)]

[!--- use lazy expression to find key ---]
[cfset arWork = XMLSearch(xmlobj, "//key")]
[cfdump var="#arWork#"]


 

 

Calendar

 
Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Search This Site

 
This is an exact search only

About This Site

 
I live west of Boston and work as a Software Engineer with ColdFusion and Flex, specializing in Linux. Recently I graduated in Professional Digital Photography from CDIA.
More about me

Recent Entries

 
Recent Tweets for Wed July 01..
I'm Twittering This! or Dude..

Recent Comments

 
Posted By Marie Payne:
I really enjoyed those pictures you took in Alaska. Thank you so much for sharing, for I've been ill and had major surgery. I can't get out and do t ...

Posted By Julia:
First time that I see such photos. Too bad that I don't have Photoshop. I have to ask somebody to do them for me.

Posted By Steven Erat:
See also this collection of images from Dewey Mclean of his experience in the Army in the Korean War [link] His photographs and maps are very we ...

recently played

 
Zombie Nation
by Future Shock
on 90's Club Hits Reloaded
90's Club Hits Reloaded, Future Shock

now playing, a plug-in for itunes

Categories

 
RSS Adobe (31)
RSS Bicycling (9)
RSS Blogging (38)
RSS Books (13)
RSS Breeze (12)
RSS CFMX Podcasts (10)
RSS ColdFusion (420)
RSS Computer Technology (51)
RSS Events (25)
RSS Flash (3)
RSS Flex (20)
RSS Gadgets (10)
RSS HiTech Industry (16)
RSS Java (25)
RSS Learning (57)
RSS Linux (70)
RSS Mac OS X (22)
RSS Macromedia (27)
RSS Meetup (34)
RSS New England (62)
RSS Odds & Ends (25)
RSS Outdoors (32)
RSS Personal (29)
RSS Photography (110)
RSS Photoshop (29)
RSS Podcasts (18)
RSS Rants (19)
RSS Restaurants (8)
RSS Science (34)
RSS Spain (16)
RSS Travel (42)
RSS Twitter (2)
RSS Video (20)
RSS Webcam (3)
RSS Writing (10)

Blogs I Read

 
Terrence Ryan
Ben Forta
Ray Camden
Kinky Solutions
Dan Vega
Gary Gilbert
Simeon Bateman
Red Hat Blogs
O'Reilly Digital Media
O'Reilly Radar
John Nack
The Strobist
Scott Kelby
Matt Kloskowski
Joe McNally
Digital Photography School
Engadget
Science Blog

RSS

 


Add to Google
Add to My Yahoo!

Aggregated By

 


Consumed By Feed-Squirrel.com
Aggregated by ColdFusionBlogger.org

Credits and Stuff

 
BlogCFC - Free ColdFusion Powered Blog Software
CJM Group - ColdFusion Website Hosting


 
 
blog | photos | flickr | referers | webcam | stats | about | contact
 
Copyright © 2009 Steven Erat. All rights reserved.
This is a personal weblog. The opinions expressed here represent my own and not those of my employer