views:

155

answers:

3

I have an xml document like this:

<?xml version="1.0" encoding="utf-8" ?>
<demographics>
    <country id="1" value="USA">
        <state id ="1" value="California">
            <city>Long Beach</city>
            <city>Los Angeles</city>
            <city>San Diego</city>
        </state>
        <state id ="2" value="Arizona">
            <city>Tucson</city>
            <city>Phoenix</city>
            <city>Tempe</city>
        </state>
    </country>
    <country id="2" value="Mexico">
        <state id ="1" value="Baja California">
            <city>Tijuana</city>
            <city>Rosarito</city>            
        </state>
    </country>
</demographics> 

How do I setup LINQ queries for doing things like: 1. Get All Countries 2. Get All States in a Country 3. Get All Cities inside a state of a paricular country ?

I gave it a try and I am kind of confused when to use Elements["NodeName"] and Descendants etc. I know I am not the brightest XML guy around. Is the format of the XML file even correct for simple traversal?

+1  A: 

Like this:

var countries = document.Root.Elements("country");
var states    = country.Elements("state");
var cities    = state.Elements("city");
SLaks
+3  A: 

To load the document from a file:

XDocument document = XDocument.Load("input.xml");

To get all the countries' names:

IEnumerable<string> countries = document
    .Descendants("country")
    .Select(element => element.Attribute("value").Value);

To get all the states that are inside the country "USA":

IEnumerable<string> states = document
    .Descendants("country")
    .Where(element => element.Attribute("value").Value == "USA")
    .Elements("state")
    .Select(element => element.Attribute("value").Value);

To get all the cities inside USA/California:

IEnumerable<string> cities = document
    .Descendants("country")
    .Where(element => element.Attribute("value").Value == "USA")
    .Elements("state")
    .Where(element => element.Attribute("value").Value == "California")
    .Elements("city")
    .Select(element => element.Value);

You might also want to look at XPath queries (you need using System.XML.XPath):

IEnumerable<string> cities = document
    .XPathSelectElements("/demographics/country[@value='USA']/state[@value='California']/city")
    .Select(element => element.Value);
Mark Byers
+1  A: 
var doc = XDocument.Load("myxml.xml");


var countries = doc.Descendants("country")
                   .Attributes("value")
                   .Select(a => a.Value);

var states    = doc.Descendants("country")
                   .Single(country => country.Attribute("value").Value == "USA")
                   .Elements("state")
                   .Attributes("value")
                   .Select(a => a.Value);

var cities    = doc.Descendants("state")
                   .Single(state => state.Attribute("value").Value == "California")
                   .Elements("city")
                   .Select(e => e.Value);

The result will have countries, states, and cities as type IEnumerable<string>.

Also worth noting that execution (i.e. parsing) will be delayed until you actually enumerate the values in those IEnumerable<string> variables. This can sometimes cause unintended performance issues. For example, if you're planning to display all the data anyway, and you databind it to some UI control, the user interface can get sluggish as it realizes that it does need to parse this after all. (It might even block the UI thread, instead of your worker thread? Not sure.) To fix this, add .ToList() to the end, to get non-deferred List<string>s instead.

Domenic