Thursday, June 5, 2014

LINQ - LINQ to XML

Intro


This week we'll build on the foundation we learned in part 1 and part 2, adding LINQ to XML to our repertoire. As you have probably surmised, LINQ to XML technology lets you use the LINQ query syntax, with which you've now familiarized yourself, to query data from an in-memory representation of XML data. To an extent, this frees you from learning even more technology for XML parsing such as XPath, so you can have a common framework of knowledge (LINQ) to query many different types of things (objects, XML, and next week SQL data). Perhaps I should have stated and emphasized this better up-front in part 1 to drum up more interest in LINQ, but learning LINQ is almost like a 3-for-1 deal. You learn LINQ, and you get to query 3 types of data. It saves code and saves time, and what else can you ask for in the world of coding? No it won't write your code for you. I know you were gonna ask :)

Loading XML

Step 1 in our journey today is loading an XML string from memory. There are other ways to get XML into a LINQ to XML object such as loading from disk, creating the xml object tree ourselves, etc, but this is the simplest. Note that you'll need to add System.Xml.Linq to your using clause. Here is how to load XML from a string:

private XElement ZooXml
        {
            get 
            {
                return XElement.Parse("<zoo><show name='Early'><animal name='Simba' animalType='lion'></animal><animal name='Bill' animalType='baboon'></animal></show><show name='Lunch'><animal name='Bjork' animalType='baboon'>Bjork says bye!</animal></show><show name='Evening'><animal name='Pete' animalType='panda'></animal></show></zoo>");
            }
        }



The only thing noteworthy here is XElement.Parse. XElement is the class you'll work with most frequently in LINQ to XML as it represents an XML element. The parse method parses the string that you pass in and returns a new object of type XElement. Pretty simple stuff so far.


Querying Basic Data

I know you all paid the price of admission to see the real show, so let's start querying the data. What I want is to pull a list of all the shows and display the names of all the shows. Here's our first example:

        protected void btnLinqXmlMultipleElements_Click(object sender, EventArgs e)
        {
            XElement zooElement = ZooXml;
            var shows = from show in zooElement.Elements("show")
                        select show;
            foreach (var show in shows)
                Response.Write("< br />" + show.Attribute("name").Value);
        }


and here's the output:
Early
Lunch
Evening

Pretty simple code huh? The .Elements() method will return all matching elements of the specified element/node (in this case zooElement, which is the root element). We specify that we only want elements named show in this case, so we only get back show elements. Then we loop through the result set and display the name of each show.

Elements Containing Specific Sub-Elements

What if we wanted only shows that have a baboon in them?

        protected void btnLinqXmlShowsWithBaboons_Click(object sender, EventArgs e)
        {
            XElement zooElement = ZooXml;
            var shows = from show in zooElement.Elements("show")
                        where show.Elements("animal").Any(animal => animal.Attribute("animalType").Value.Equals("baboon", StringComparison.OrdinalIgnoreCase))
                        select show;
            foreach (var show in shows)
                Response.Write("< br />" + show.Attribute("name").Value);
        }



And our output (hey, it's even correct!) is as follows:
Early
Lunch

As you can see above, the where clause is what's different. We tell the compiler that we want all show elements who contain an element named "animal" which have an attribute "animalType" with the value "baboon". There's nothing complicated here, with a little practice you can get the hang of it quite easily.

Getting Element Value(s)

Scroll back up near the top for a moment where we defined the property ZooXml. Notice how Bjork the baboon has a value within her xml element, with the value "Bjork says bye!"? Putting values into xml tags is common so let's see filter on and use them:

        protected void btnLinqXmlElementWithValue_Click(object sender, EventArgs e)
        {
            XElement zooElement = ZooXml;
            var elements = from element in zooElement.Descendants()
                            where !String.IsNullOrWhiteSpace(element.Value) && element.Name.ToString().Equals("animal", StringComparison.OrdinalIgnoreCase)
                            select element;
            foreach (var element in elements)
                Response.Write("< br />" + element.Name + "::" + element.Attribute("animalType").Value + "::" + element.Attribute("name").Value + "::" + element.Value);
        }



Output:
animal::baboon::Bjork::Bjork says bye! 

The first thing you'll notice different is that we used Descendants() this time instead of Elements(). What's the diff yo? Elements are just the direct children of where we're looking. For our prior queries that was good enough. For this query, we know that an animal is what has a value, and an animal is not a direct child of the root zoo element. Rather, animals are children of the show in which they perform, so we need to use the Descendants method().

The next difference is our use of element.Value. This isn't voodoo or anything, it's exactly what you think; the element value. We use it in our where clause to pull only those elements that have a value.

What's Next?

There are lots more things that you can do with LINQ to XML. We don't have time to cover all of them, but take a look on MSDN; there's lots to read.
  • Other methods such as Ancestors(), ElementsAfterSelf(), etc.
  • XNode: look it up! It's powerful. Finer level of detail than XElement, though you won't often need it's functionality.
  • Manipulate and save XML.
  • Create an XML tree entirely in code.
  • Peek ahead to LINQ to SQL which will be next week's post.
  • Experiment with last week's topics while playing with LINQ to XML. Joins, ordering, grouping, projections. This is by far the best thing you could do, as you'll put the pieces of the puzzle together and learn them as a whole.

Resources

LINQ to XML on MSDN

No comments:

Post a Comment