Enumerating Through XML Elements Using LINQ-to-XML

Posted by: Scott on Writing, on 28 Sep 2010 | View original | Bookmarked: 0 time(s)

4Guys reader Dan D. recently emailed me with an inquiry surrounding my article series, Building a Store Locator ASP.NET Application Using Google Maps API, specifically on how to access a different set of XML elements within the XML data returned from the Google Maps APIs geocoding service. Googles geocoding service is offered as a URL that, when requested, returns information about a particular address. For instance, if you point your browser to http://maps.google.com/maps/api/geocode/xml?address=1600+Pennsylvania+Ave,+Washington+D.C.&sensor=false you should see an XML response that indicates whether the address is valid, the formatted address, the components that make up the address, and geographical information about the address, including the latitude and longitude coordinates.

This geocoding service is used by the Store Locator application in two ways:

  1. To validate the user-entered address. If the user enters an ambiguous address, like Springfield, then the geocoding service will return possible matches. These are displayed to the user, allowing her to choose which address she meant.
  2. To determine the latitude and longitude coordinates of the user-entered address. These coordinates are used to retrieve those stores that are nearby.

The Store Location application includes a method named GetGeocodingSearchResults that, when called, makes an HTTP request to the geocoding service and returns the results as an XElement object, one of the key components of LINQ-to-XML.

Dans question follows:

I have a question with regards to accessing the elements contained within the address_components[] array.  Specifically, I would like to return the long and short names for locality and country.  I was wondering if you could post an small article on how to iterate through the XML array components loaded into the XElement.

The address_components[] array Dan refers to is the set of <address_component> elements returned by the geocoding service. Again, visit http://maps.google.com/maps/api/geocode/xml?address=1600+Pennsylvania+Ave,+Washington+D.C.&sensor=false. Note how there are multiple <address_component> elements detailing the type and long and short names for each component of the address. For the address 1600 Pennsylvania Ave, Washington D.C. there are the following address components:

<result>
  <address_component>
   <long_name>1600</long_name>
   <short_name>1600</short_name>
   <type>street_number</type>
  </address_component>
  <address_component>
   <long_name>Pennsylvania Ave NW</long_name>
   <short_name>Pennsylvania Ave NW</short_name>
   <type>route</type>
  </address_component>
  <address_component>
   <long_name>Washington</long_name>
   <short_name>Washington</short_name>
   <type>locality</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Washington</long_name>
   <short_name>Washington</short_name>
   <type>administrative_area_level_3</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>District of Columbia</long_name>
   <short_name>District of Columbia</short_name>
   <type>administrative_area_level_2</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>District of Columbia</long_name>
   <short_name>DC</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>United States</long_name>
   <short_name>US</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>20500</long_name>
   <short_name>20500</short_name>
   <type>postal_code</type>
  </address_component>
  ...
</result>

Note that each <address_component> element has a <long_name> and <short_name> child element, and one or more <type> child elements.

To simple iterate through each <address_component> element we could use the following code:

var results = GoogleMapsAPIHelpersCS.GetGeocodingSearchResults(address);var addressComponents = results.Element("result").Elements("address_component");
foreach (var component in addressComponents)
{
    var longName = component.Element("long_name").Value;
    var shortName = component.Element("short_name").Value;

    var types = new List<string>();
    foreach (var type in component.Elements("type"))
        types.Add(type.Value);

    // At this point you can do whatever it is you want to do 
    // with the longName, shortName, and types information for
    // this component...
    if (types.Contains("locality") || types.Contains("country"))
        Response.Write(string.Format("<p>LongName = {0}, ShortName = {1}, Types = {2}</p>",
                                    longName, 
                                    shortName, 
                                    string.Join(", ", types.ToArray())
                                )
                    );
}

Here, we reference the set of <address_component> elements using results.Element(result).Elements(address_components), where results is the XElement object returned from the GetGeocodingSearchResults method. The Element(results) call gets a reference to the <result> XML element, while Elements(address_component) gives us the enumerable collection of <address_component> elements, which we then can loop through.

Inside the loop we get the values of the <long_name> and <short_name> XML elements and then loop through the set of <type> elements, the value of each to a List of strings (types). Finally, we can do what Dan is interested in doing determine if the address component is for the locality or country and, if so, do something with the long and short names. Here, I simply display them via a Response.Write statement.

Another option is to use LINQ to create an anonymous type that models the information of interest. The following statement creates a variable named addressComponents2 that is an enumeration of anonymous objects that have three properties: LongName, ShortName, and Types, which contain the values of the <long_name>, <short_name>, and <type> elements for each <address_component>.

var results = GoogleMapsAPIHelpersCS.GetGeocodingSearchResults(address);

var addressComponents2 =
        from component in results.Element("result").Elements("address_component")
        select new
        {
            LongName = component.Element("long_name").Value,
            ShortName = component.Element("short_name").Value,
            Types = (from type in component.Elements("type")
                        select type.Value).ToArray()
        };

We can now filter the results using the Where method:

var filteredAddressComponents = addressComponents2
                                    .Where(addr => addr.Types.Contains("locality") ||
                                                    addr.Types.Contains("country"));

And now enumerating over filteredAddressComponents returns just those address components for the locality or country types. The following loop walks through each of these and emits the LongName, ShortName, and Types property values. Note how these are actual properties and not strings, meaning we have strong typing, which brings with it the benefits of IntelliSense and compile-time support.

// At this point you can use a foreach loop to 
// walk through the various components
foreach (var addr in filteredAddressComponents)
{
    Response.Write(string.Format("<p>LongName = {0}, ShortName = {1}, Types = {2}</p>",
                                addr.LongName, 
                                addr.ShortName, 
                                string.Join(", ", addr.Types.ToArray())
                            )
                );
}

Happy Programming!

Category: XLinq | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 2752 | Hits: 16

Similar Posts

  • A Bad Idea, EF Entities over WCF more
  • From LINQ to XPath and Back Again more
  • "The security validation for this page is invalid" when calling the SharePoint Web Services more
  • XML and Languages more
  • XmlSchemaSet Thread Safety more
  • Converting from XmlDocument to XDocument more
  • Twitter API - Get a list of your friends in C# more
  • Calling the Twitter API in C# more
  • XML serialization using generics more
  • An Extensive Examination of LINQ: An Introduction to LINQ more

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD