Thursday, August 02, 2007
While working on our LINQ book I've come to really enjoy LINQ to XML. I've worked with a lot of XML API's over the years, however, the LINQ to XML API is my favorite...by far. In addition to providing all the nice query facilities made available by LINQ, it also provides a lot of other great features that many people overlook. As I promised long ago, I'm going to begin to talk about the things that I enjoy about LINQ to XML in hopes that it will help you realize that the red headed step child of LINQ has some things to offer the world as well. :)

One of the most common things that we need to do when dealing with XML is transform it. We're usually transforming it into an alternate XML format, or transforming the XML into a set of objects. In this post I'm going to quickly look at some of the transformation capabilities offered by LINQ to XML. To help us get started I'm going to use the following XML which is the XML representation of a contact in Highrise.

<?xml version="1.0" encoding="UTF-8"?>
<person>
  <author-id type="integer">1436</author-id>
  <background/>
  <company-id type="integer">1226900</company-id>
  <created-at type="datetime">2007-06-09T03:13:15Z</created-at>
  <first-name>Steve</first-name>
  <group-id type="integer"/>
  <id type="integer">1226899</id>
  <last-name>Eichert</last-name>
  <owner-id type="integer"/>
  <title/>
  <updated-at type="datetime">2007-06-09T03:15:16Z</updated-at>
  <visible-to>Everyone</visible-to>
  <contact-data>
    <email-addresses type="array">
      <email-address>
        <address>steve.eichert at google mail dot com</address>
        <id type="integer">559722</id>
        <location>Work</location>
      </email-address>
    </email-addresses>
    <web-addresses type="array">
      <web-address>
        <id type="integer">942962</id>
        <location>Work</location>
        <url>http://iqueryable.com/</url>
      </web-address>
    </web-addresses>
  </contact-data>
</person>

Rather than be stuck with our contact in XML, let's see what we can do to transform the above XML into the hCard microformat. We're going to ignore a bunch of data, such as all the ids, since it doesn't have any meaning outside of Highrise. When we're done we'll end up with the much simplified XML shown below:

<div class="vcard">
  <div class="fn">Steve Eichert</div>
  <div>Email: <span class="email">steve.eichert at google mail dot com</span></div>
  <a class="url" href="http://iqueryable.com/">http://iqueryable.com/</a>
</div>

The first step for transforming our Highrise XML into the hCard microformat is to load the Highrise XML into an XElement.

XElement highriseRoot = XElement.Load("highrise-contact.xml");

We use the static Load method of XElement to load the XML contained within the "highrise-contact.xml" file that we've saved locally. I don't believe the Highrise API is officially supported at the moment so I'm not going to load the contact details directly from the highrisehq.com site. Perhaps, in a future post we can explore that as an option.

Anywho, once our XML is loaded into an XElement, we can transform our Highrise XML into the hCard microformat by building a new XElement. We'll use the Element query axis method to retrieve the first and last name of the contact, and we'll embed query expressions and make use of the Descendants query axis method for selecting all the email and web addresses for the contact within the source XML. When we put it all together we end up with the C# code below:

XElement highriseRoot = XElement.Load("highrise-contact.xml");

XElement hCard =
    new XElement("div",
        new XAttribute("class", "vcard"),
        new XElement("div",
            new XAttribute("class", "fn"),
            highriseRoot.Element("first-name") + " " + highriseRoot.Element("last-name")
        ),
        from emailElement in highriseRoot.Descendants("email-address")
        select new XElement("div",
            "Email:",
            new XElement("span",
                new XAttribute("class", "email"),
                (string) emailElement.Element("address")
            )
        ),
        from webElement in highriseRoot.Descendants("web-address")
        select
        new XElement("a",
            new XAttribute("class", "url"),
            new XAttribute("href", (string) webElement.Element("url")),
            (string) webElement.Element("url")
        )
    );

Console.WriteLine(hCard);

At first glance, the above code might be overwhelming. However, once you come to understand the power of functional construction you'll quickly realize how wonderful LINQ to XML can be for transforming XML to alternate XML formats. In addition to making it easy to transform XML into alternate XML formats, LINQ to XML also makes it very easy to transform XML into objects. If we have a Contact class defined as:

public class Contact {
    public string Name { get; set; }
    public IEnumerable<string> EmailAddresses { get; set; }
    public IEnumerable<string> Urls { get; set; }
}

We can transform the contact details in our XML into a Contact instance with the following code:

Contact contact = new Contact {
    Name = (string) highriseRoot.Element("first-name") + " " + (string) highriseRoot.Element("last-name"),
    EmailAddresses = highriseRoot.Descendants("email-address").Select(e => (string)e.Element("address")),
    Urls = highriseRoot.Descendants("web-address").Select(e => (string)e.Element("url"))
};

After looking back at the sample here I wish I had chosen an XML fragment with a little more hierarchy, however it's much too late for that now. Hopefully, the code included in this post gives you a small taste of the types of XML transformations possible with LINQ to XML. As you begin to work with LINQ to XML, you'll find that functional construction, combined with query axis methods, and query expressions provide a tremendous amount of flexibility for transforming XML. Additionally, the new object initializer syntax and LINQ to XML's ability to easily construct objects from XML makes it very easy to create objects from XML. I've attached a zip file with the code above to this post. (VS2008 Beta 2 Required) LINQtoXMLTransformSample.zip (23.69 KB)

Friday, August 03, 2007 3:20:02 AM (Eastern Daylight Time, UTC-04:00) | Comments [0] |  |  | #
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Search
Archive
Links
Categories
Admin Login
Sign In
Blogroll
Themes
Pick a theme: