Handling XML Namespaces Using LINQ

Have you ever worked with XML documents that have Namespaces with them? It's that extra bit of markup in XML documents that is supposed to prevent any of your elements from conflicting with each other. For me, they tend to get in they way more than they help -- Let's take some example XML.

<?xml version="1.0"?>
<Inventory xmlns:data="http://www.bookstore.com/" >
    <Books xmlns:inv="http://www.bookstore.com/book-inventory">
        <Book count="231" >C# For Dummies</Book>
        <Book count="35" >Using jLinq</Book>
        <Book count="54" >Ruby On Raylze</Book>
    </Books>
</Inventory>

Typically when using XML I've used XPaths to find the information I need. I'm certainly not an expert but I know enough to get the job done. Normally, with plain vanilla XML, you could write a quick XPath to find your books and parse over them. There is a handful of ways you could do it but for this example let's be a little lazy...

XDocument inventory = XDocument.Load("d:\\temp\\sample.xml");
IEnumerable<XElement> books = inventory.XPathSelectElements("//Book");

//How many books do you think we have?
Console.WriteLine(books.Count());

If you run this code you're going to end up with a big, fat, ugly zero. You can even try a variety of other XPaths, but none of which will find you the data you want.

  • //inv:Book -- throws an error
  • Inventory/Books/Book -- returns 0
  • Inventory/Books/* -- returns 0
  • //Books/* -- returns 0

Nothing -- Ouch...

LINQ To The Rescue

If you've been using LINQ then you know that you can't help but use it when you realize just how powerful it is. As much as I used it though, it never occurred to me that LINQ could be used in place of an XPath. Using the same XML as before and applying a tiny bit of LINQ magic, we come up with the following solution.

var results =
    inventory.Descendants()
        .Where(o => o.Name.LocalName.Equals("Book"))
        .Select(o => new {
            Name = o.Value,
            Count = int.Parse(o.Attribute("count").Value)
        });

I didn't do any performance tests but I doubt that something like this would incur any sort of noticeable hit. Even if it is a bit slower, I think it is worth it. With a single LINQ query I'm not only able to avoid dealing with Namespaces all together, but I'm able to parse and return my data into an anonymous type. All my information is ready to go just like that!

This certainly isn't a monumental discovery -- In fact I'm sure that I'm arriving to this late in the game, but for today my life got a lot better now that it became much less painful to work with SOAP responses!

August 5, 2009

Handling XML Namespaces Using LINQ

Performing simple queries with XML and LINQ.