Building an XML file with Google’s Weather and LINQ to XML
|
I had a need to create an xml file that contains tomorrow's weather forecasts for selection of addresses in a database. I spent way too much time working this out, so thought I would share for the next dev down the road. The first step, which took quite a while, was to find an RSS feed or web service that supplies the forecasts. Much time was wasted chasing down dead ends, for example a no-longer-existing API from Google for which you will find lots of references and how-to's if you search the web. In the end, I decided to go with http://www.google.com/ig/api?weather which returns some simple forecast info given a location (us zip code, city/state, city/country and other combinations. Now I could use my address data to dynamically build URIs which I can then call using XElement.Load (no bothering with web services or WebRequest/WebResponse). After calling each URI, I use LINQ to XML to get the forecast from the results and then build a struct which I add to a list. When I have iterated through all of the addresses, I use LINQ to XML one last time to create the final XML and then persist that to a file. This file is something I want to leverage in a sample in the new version of my book Programming Entity Framework, so I am using that data. I have a Lodgings entity that is realted to Contact which is related to Addresses. So for each lodging, I extract the critical address information. 1: var lodgingCityCountry =
2: select new
3: {
4: l.LodgingID,
5: l.Contact.Addresses.FirstOrDefault().City,
6: l.Contact.Addresses.FirstOrDefault().StateProvince,
7: l.Contact.Addresses.FirstOrDefault().CountryRegion
8: };Next I iterated through each result and build a uri. I have to be careful not to pass in nulls and to build appropriate uris. 1: foreach (var l in lodgingCityCountry)
2: {
3: string uri;
4: if ((l.StateProvince == null && l.City == null
5: {
6: string region;
7: if(l.CountryRegion !=null)
8: {region= "," + l.CountryRegion.Trim();
9: }
10: else
11: { region="";
12: }
13: if (l.StateProvince != null)
14: {
15: uri = "http://www.google.com/ig/api?weather=" +
16: }
17: else
18: {
19: uri = "http://www.google.com/ig/api?weather=" +
20: }Next, load the results into an XElement. 1: System.Xml.Linq.XElement googleRSS = System.Xml.Linq.XElement.Load(uri);
Here's what the XML looks like. It contains the current conditions and forecsat info for current day and the next three days. <xml_api_reply version="1"> <weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0"> <forecast_information> <city data="Tucson, AZ" /> <postal_code data="Tucson,Arizona,United States" /> <latitude_e6 data="" /> <longitude_e6 data="" /> <forecast_date data="2009-09-28" /> <current_date_time data="2009-09-29 00:11:57 +0000" /> <unit_system data="US" /> </forecast_information> <current_conditions> <condition data="Clear" /> <temp_f data="102" /> <temp_c data="39" /> <humidity data="Humidity: 12%" /> <icon data="/ig/images/weather/sunny.gif" /> <wind_condition data="Wind: SW at 4 mph" /> </current_conditions> <forecast_conditions> <day_of_week data="Mon" /> <low data="68" /> <high data="103" /> <icon data="/ig/images/weather/sunny.gif" /> <condition data="Clear" /> </forecast_conditions> I only want some of the forecast info for tomorrow so I use LINQ to XML to grab them. 1: var items = from item in googleRSS.Elements("weather")
2: .Elements("forecast_conditions")
3: select item;Now that's all of the forecast items. I only want the first one, but I have to be careful in case there is no data so the next steps are wrapped in a try/catch. I grab the second one (the first is today's forecast) 1: if (items.Count > 0)
2: {
3: var tomorrow = items.ToList()[1];Then I create a new LodgingForecast struct (I'll show the definition of this at the end) from the ID of the lodging and a concatenated string of forecast information. 4: forecasts.Add(new LodgingForecast
5: {
6: lodgingID = l.LodgingID,
7: forecast = (String)tomorrow.Element("condition").Attribute("data").Value
8: + ", Low: " + (String)tomorrow.Element("low").Attribute("data").Value
9: + ", High: " + (String)tomorrow.Element("high").Attribute("data").Value
10: });
11: }
After closing the remaining braces, I will have finished iterating through the addresses and building my list of forecasts for each Lodging. I use LINQ to XML one last time to create a nice XML file based on the list. 1: XElement forecastsXML = new XElement("Forecasts",
2: from f in forecasts
3: select new XElement("Lodging",
4: new XAttribute("ID", f.lodgingID),
5: new XAttribute("forecast", f.forecast))); Finally I create an XDocument from this XElement and save it to a file. 1: Document doc = new XDocument(
2: new XDeclaration("1.0", "utf-8", "yes"),
3: new XComment("LodgingForecasts"),
4: forecastsXML);
5: doc.Save(@"LodgingForecasts.xml"); Now the app can cache this data once a day and access it locally without having to requery the web. Here's the struct referenced earlier. 1: struct LodgingForecast
2: {
3: public int lodgingID;
4: public String forecast;
5: } |

