In a previous article, I noted that I would provide ASP.NET code to consume the National Weather Service’s XML current observation feeds. Here it is.
In the case of PHP, I opted to create a class that would provide a simple array of values from the feed, as well as some predefined outputs for the feed (so far, just an HTML table; in a soon-to-be-released update, we’ll include DIV tags).
In the case of ASP.NET, I may yet build a custom control that creates an image with current weather. But ADO.NET — Microsoft’s classes for data manipulation — makes working directly with the XML feed, and using that data to bind to DataGrids and the like, very simple and direct.
That is, provided you can get the XML feed to work with the XMLDataSource control.
The XMLDataSource control expects each record to be a single node, and each field to be an attribute of the node. That means one node per record, instead of one node per field.
For example, this kind of XML node will work as a single record with XMLDataSource:
<current_observation weather="Fair" temp_f="60" wind_mph="10" />
But these multiple XML nodes would be considered multiple records:
<current_observation> <weather>Fair</weather> <temp_f>60</temp_f> <wind_mph>10</wind_mph> </current_observation>
We somehow need to convert the nodes-only data we get from the NWS to the attributes-only data the XmlDataSource wants. That’s easily accomplished via XSL, or Extensible Style Sheets.
The first step in creating an XSL is to declare its namespace.
<?xml version="1.0" encoding="iso-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
That done, we now need to create the parent node for the feed, and indicate that we want to apply our template to the same parent node in the XML feed:
<xsl:template match="/"> <current_observation> <xsl:apply-templates /> </current_observation> </xsl:template>
Then, we need to inform the XSL which node of the original XML feed indicates the beginning of a record; that is, which child nodes of the XML document that will act as records, and that themselves have the child nodes that will act as fields / attributes for the record.
In NWS feeds, each XML document contains only one record, so the parent record node for the XSL template to match is, conveniently, the parent node of the feed.
With that out of the way, we can begin converting our XML. To do that, we simply create a new node; in this case, I’m calling the node nwsobs.
Then, for each node in the NWS XML feed, we simply reassign it as an attribute of the node:
<xsl:attribute name="location"> <xsl:value-of select="location"/> </xsl:attribute>
Here’s how the code above works:
- We indicate we are going to create a new attribute for the node named “location”
- We indicate the value of that attribute is the same as the value of the location node in the XML document
You can do more than just convert a node to an attribute using XSL. You can also reform the data to read differently, by appending additional text:
<xsl:attribute name="visibility_mi"> <xsl:value-of select="visibility_mi"/> <xsl:text> miles</xsl:text> </xsl:attribute>
You can even use the equivalent of if-then tests, conditioning whether to convert a node to an attribute, or set the value of an attribute based on some condition:
<xsl:attribute name="visibility_mi"> <xsl:choose> <xsl:when test="visibility_mi = 10.00"> <xsl:text>Unlimited</xsl:text> </xsl:when> <xsl:otherwise> <xsl:value-of select="visibility_mi"/> <xsl:text> miles</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:attribute>
Once your XSL is squared away, you can now provide it to your XmlDataSource control, using the TransformFile property. In the example below, I am presuming you put your XSL file, named Weather.xsl, in the App_Data directory, and you call the DataFile directly from the NWS Web site:
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="http://www.weather.gov/data/current_obs/KAUG.xml" TransformFile="~/App_Data/Weather.xsl" />
You may find that your Web server throws an error when trying to connect to a remote XML feed (or any URL) from your Web server in this way. The error may range from stating that the connection was unexpectedly closed, to the address not being able to be resolved, or something similar.
Those errors are usually caused by your Web host’s proxy settings; most Web hosts, especially those providing shared hosting, have complex internal networks that require the use of proxy servers to both manage traffic and prevent intrusions.
You can almost always resolve proxy errors by changing the defaultproxy element in your web.config file. Usually, it’s enough to change the enabled attribute to false. Sometimes, you need to change the useDefaultCredentials attribute to true. Rarely, you’ll need to add full proxy info that you’ll need to get from your Web host (and if that’s the case, your Web host is way too uptight or way too lazy; either way, it’s time to find a new host).
You also may not be happy with the delay in trying to get the XML feed via a HTTP request, direct from your Web server; there may be significant delay, and your entire page’s load will be held up while the XmlDataSource waits for a reply from the NWS.
A way around that is to cache the XML on your site, picking it up remotely only if the current observation has expired. The code to do that will be the subject of another article.
I distribute code under the GNU GPL version 3.
Before I close, I want to point out that much of this article is possible thanks to Raj Kaimal’s article on ASP.NET and XSL.