Steven Erat's Blog Steven Erat Photography
 
 
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

topics

 
adobe blogging coldfusion computer technology events flex java learning linux mac os x macromedia meetup new england odds & ends outdoors personal photos photoshop science travel video

About This Site

 
Adobe Alumni & Community Professional. Expert in ColdFusion, Flex, LCDS, Photoshop, Lightroom. Linux RHCE. Follow Me!. For my photography check out Boston Portrait Photographer.

Speaker at CF.Objective(): Automated UI Testing with CFSelenium, MXUnit, ANT, and JenkinsCI

Adobe Community Professional (ACP)
Red Hat Linux Certified Engineer

Recent Entries

 
Automated System Testing for ..
Could not find ColdFusion com..
No April Fools: Selenium Ship..

Recent Comments

 
Posted By Steven Erat:
Jim, and anyone else that may attend, if you would like the full slide deck and my demo project files BEFORE the conference, please reply as a comment ...

Posted By Jim Priest:
Can't wait for this one!!

Posted By iPhone Repair:
It appears there are so many people have issue with their iPhone & iPod Touch screens dropped and cracked. It happened to me also when u haven't got a ...

recently played

 
Mr. Brightside
by The Killers
on Hot Fuss
Get Hot Fuss by The Killers on Amazon

now playing, a plug-in for itunes

Categories

 
RSS Adobe (34)
RSS Bicycling (9)
RSS Blogging (39)
RSS Books (13)
RSS Breeze (13)
RSS CFMX Podcasts (10)
RSS ColdFusion (437)
RSS Computer Technology (51)
RSS Events (26)
RSS Flex (20)
RSS Gadgets (11)
RSS HiTech Industry (16)
RSS Java (26)
RSS Learning (57)
RSS Linux (70)
RSS Mac OS X (23)
RSS Macromedia (27)
RSS Meetup (35)
RSS New England (62)
RSS Odds & Ends (25)
RSS Outdoors (32)
RSS Personal (29)
RSS Photos (111)
RSS Photoshop (29)
RSS Podcasts (18)
RSS Rants (19)
RSS Restaurants (8)
RSS Science (34)
RSS Spain (16)
RSS Travel (42)
RSS Twitter (10)
RSS Video (20)
RSS Webcam (3)
RSS Writing (10)

RSS

 


Add to Google
Add to My Yahoo!

Credits and Stuff

 
BlogCFC - Free ColdFusion Powered Blog Software


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