At last nights OCCFUG meeting, one of the members asked what is the best way to access the data from nodes in an XML document that have xmlns namespace prefixes. I touched on this topic briefly in the comments of my post about reading XML documents, but I thought I would expound upon it a little more as there doesn't seem to be a lot of solid explanations out there on how to do this in ColdFusion. In a nutshell, the answer is to use XMLSearch() with an xPath expression that utilizes the namespace-uri() function.

For this example we are going to use CFFEED to retrieve weather information from the Yahoo! weather api.

<cffeed
   source = "http://weather.yahooapis.com/forecastrss?p=92708"
   xmlVar= "myXML">

The myXml variable now contains the following xml:

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
<channel>
<title>Yahoo! Weather - Fountain Valley, CA</title>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/Fountain_Valley__CA/*http://weather.yahoo.com/forecast/92708_f.html</link>
<description>Yahoo! Weather for Fountain Valley, CA</description>
<language>en-us</language>
<lastBuildDate>Fri, 26 Sep 2008 12:53:00 GMT</lastBuildDate>
<ttl>60</ttl>
<image>
<title>Yahoo! Weather</title>
<url>http://l.yimg.com/us.yimg.com/i/us/nws/th/main_142b.gif</url>
<link>http://weather.yahoo.com</link>
<width>142</width>
<height>18</height>
<width>142</width>
<height>18</height>
</image>
<item>
<title>Conditions for Fountain Valley, CA at 8:53 am PDT</title>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/Fountain_Valley__CA/*http://weather.yahoo.com/forecast/92708_f.html</link>
<geo:lat xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">33.71</geo:lat>
<geo:long xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">-117.95</geo:long>
<yweather:condition xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" text="Partly Cloudy" code="30" temp="66" date="Fri, 26 Sep 2008 8:53 am PDT" />
<yweather:forecast xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" day="Fri" date="26 Sep 2008" low="65" high="79" text="Partly Cloudy" code="30" />
<yweather:forecast xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" day="Sat" date="27 Sep 2008" low="64" high="80" text="Partly Cloudy" code="30" />
<description>&lt;img src="http://l.yimg.com/us.yimg.com/i/us/we/52/30.gif"/&gt;&lt;br /&gt;
&lt;b&gt;Current Conditions:&lt;/b&gt;&lt;br /&gt;
Partly Cloudy, 66 F&lt;BR /&gt;
&lt;BR /&gt;&lt;b&gt;Forecast:&lt;/b&gt;&lt;BR /&gt;
Fri - Partly Cloudy. High: 79 Low: 65&lt;br /&gt;
Sat - Partly Cloudy. High: 80 Low: 64&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://us.rd.yahoo.com/dailynews/rss/weather/Fountain_Valley__CA/*http://weather.yahoo.com/forecast/USCA0399_f.html"&gt;Full Forecast at Yahoo! Weather&lt;/a&gt;&lt;BR/&gt;
(provided by The Weather Channel)&lt;br/&gt;</description>
<pubDate>Fri, 26 Sep 2008 12:53:00 GMT</pubDate>
<guid isPermaLink="false">92708_2008_09_26_8_53_PDT</guid>
</item>
</channel>
</rss>

You can see in that xml that there is a "geo" namespace that is used for the latitude and longitude nodes, and there is a namespace called "yweather" that is used in the nodes that hold the current and forecasted weather conditions. XML spaces always have a Uniform Resource Identifier (URI) associated with them, and the URI looks just like a URL that you would use to browse to a web page. You will need to use this URI in your xPath expression in order to find any node that has a namespace prefix.

This example shows how to write an xPath expression to find all the nodes with a specific namespace URI, and how to find nodes within a namespace that have a specific local name. (click here to see the results of this code)

<cffeed source = "http://weather.yahooapis.com/forecastrss?p=92708"
   xmlVar= "myXML">


<b>Get all the Nodes with the yweather namespace</b>
<cfset yWeatherNodes = xmlSearch(myXML,"//*[namespace-uri()='http://xml.weather.yahoo.com/ns/rss/1.0']")> <cfdump var="#yWeatherNodes#">

<b>Only get the yweather:forecast nodes</b>
<cfset yWeatherForecastNodes = xmlSearch(myXML,"//*[local-name()='forecast' and namespace-uri()='http://xml.weather.yahoo.com/ns/rss/1.0']")> <cfdump var="#yWeatherForecastNodes#">

<b>Get the nodes with the geo namespace</b>
<cfset geoNodes = xmlSearch(myXML,"//*[namespace-uri()='http://www.w3.org/2003/01/geo/wgs84_pos##']")> <cfdump var="#geoNodes#">

From there you would just loop through the array of nodes that gets returned by the xmlSearch() function and do whatever you need to with the data. The following example outputs all the data from the yweather:forecast nodes (click here to see the results of this page):

<cffeed source = "http://weather.yahooapis.com/forecastrss?p=92708"
   xmlVar= "myXML">


<cfset yWeatherForecastNodes = xmlSearch(myXML,"//*[local-name()='forecast' and namespace-uri()='http://xml.weather.yahoo.com/ns/rss/1.0']")> <cfoutput>
<h3>#arraylen(yWeatherForecastNodes)# day weather forecast for 97208</h3>
<cfloop from="1" to="#arraylen(yWeatherForecastNodes)#" index="i">
<cfset forecast = yWeatherForecastNodes[i].XmlAttributes>
   <b>#forecast.day#, #dateformat(forecast.date,"mm/dd/yyyy")#</b><br>
   #forecast.text#<br>
   High: #forecast.high#<br>
   Low: #forecast.low#<br>
   <br>
</cfloop>
</cfoutput>

XMLSearch() rocks! I remember before it arrived in ColdFusion MX and I would have to do all kinds of nested cfloops to get at nodes that were deep in an xml document. It's a big time saver once you learn how to write the xPath expressions. To learn more about them, visit http://www.w3.org/TR/xpath.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Andrew Maurer's Gravatar Beautiful, Thanks Scott!
# Posted By Andrew Maurer | 9/26/08 9:33 PM
dc's Gravatar an alternative syntax is:
[cfset forecasts = XMLSearch(myXML, "//yweather:forecasts"/]

(note: this doesn't work with the sample xml above unless you move the xmlns:yweather decleration to the root element)
# Posted By dc | 9/27/08 2:21 PM