I want to start this off saying that I don't know all that much about XML standards. All I know is that sometimes they get in my way. In particular, XML name spaces and tag prefixes can make searching ColdFusion XML objects using XPath very difficult (at least for the layman like me). For instance, take a look at this chunk of XML SOAP request that is generated by the XStandard WYSIWYG editor (I am sorting it for later use):
<!--- Store the XStandard SOAP XML. ---> <cfsavecontent variable="strXml"> <?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <doDirectorySearch xmlns="http://xstandard.com/2004/web-services"> <lang>en</lang> <searchFor>Smith</searchFor> <filterBy>staff</filterBy> </doDirectorySearch> </soap:Body> </soap:Envelope> </cfsavecontent>
Notice that several of the XML node tags begin with "soap:". Also notice that the "soap" name space is defined as "http://schemas.xmlsoap.org/soap/envelope/". Now, I have absolutely NO idea what this does. I don't understand name spaces, I don't understand prefixed tags. All I know is that because of the prefixed tags and name spaces, this ColdFusion XML search does NOT work:
<!--- Parse the XStandard SOAP request XML into a ColdFusion XML document object. Be sure to trim the XML so that it parses properly. ---> <cfset xmlRequest = XmlParse( strXml.Trim() ) /> <!--- Search for the "searchFor" XML node using XPath. ---> <cfset arrSearchNodes = XmlSearch( xmlRequest, "//searchFor" ) />
If I went to dump this out, the arrSearchNodes ColdFusion array is EMPTY. However, if you look at the w3schools XPath tutorial you will see that "//" should select all nodes in the document no matter where they are (it doesn't say anything about name spaces).
Clearly there is a node "searchFor", so why is the XPath search for "//searchFor" not working? It must have to do with the name spaces and the tag prefixes. When I asked about this on CF-Talk (a looooong time ago), someone suggested just searching for the tag name with this notation:
<!--- Search for "searchFor" XML nodes using XPath. ---> <cfset arrSearchNodes = XmlSearch( xmlRequest, "//*[name()='searchFor']" ) />
Now, this DOES work because it is searching for nodes whose node name returned by the function name() is "searchFor". Yeah, it works, but that just look / feels / tastes nasty to me. I just want to be able to search for the node using standard XPath notation (yes I know the above IS standard, but you get my point).
In order to have it my way, I am forced to use regular expressions to strip out the XML name space attributes and tag prefixes from the raw XML data before I parse it into a ColdFusion XML document:
<!--- Strip out the tag prefixes. This will convert tags from the form of soap:nodeName to JUST nodeName. This works for both openning and closing tags. ---> <cfset strXml = strXml.ReplaceAll( "(</?)(\w+:)", "$1" ) /> <!--- Remove all references to XML name spaces. These are node attributes that begin with "xmlns:". ---> <cfset strXml = strXml.ReplaceAll( "xmlns(:\w+)?=""[^""]*""", "" ) />
Doing that converts the original XML SOAP request XML to this:
<?xml version="1.0" encoding="UTF-8"?> <Envelope> <Body> <doDirectorySearch> <lang>en</lang> <searchFor>Smith</searchFor> <filterBy>staff</filterBy> </doDirectorySearch> </Body> </Envelope>
Notice that the only information that I am left with is the important information (at least, the way my unfrozen cave man lawyer brain sees it). Once we have the XML in this format, we can easily run nice looking XPath searches on the ColdFusion XML document object:
<!--- Parse the XStandard SOAP request XML into a ColdFusion XML document object. Be sure to trim the XML so that it parses properly. ---> <cfset xmlRequest = XmlParse( strXml.Trim() ) /> <!--- Search for the "searchFor" XML node using XPath. ---> <cfset arrSearchNodes = XmlSearch( xmlRequest, "//searchFor" ) /> <!--- Dump out the search results. ---> <cfdump var="#arrSearchNodes#" label="searchFor XPath Search Results" />
This gives us the following CFDump output:
Now, again, I don't know all that much about XML and its standards, but I can look at code and tell you that this search:
... looks MUCH better than this search:
Of course, if anyone knows of a way to handle XPath searches elegantly (should read "Using the //searchFor" notation) without stripping out the name space and all that jazz, please let me know.
Want to use code from this post? Check out the license.