Welcome to this installment of the .NET Nuts & Bolts column. The prior .NET Nuts & Bolts article provided an Introduction to Language Integrated Query (LINQ). This article will build on the LINQ introduction and focus on LINQ to XML for unified data access of XML-based data. You'll begin with a look at challenges with the traditional Document Object Model (DOM) means of accessing and traversing XML, and then jump in to the benefits of LINQ to XML and how it tames XML in a unified manner. This will be part two of a few articles that cover LINQ in some detail.
Challenges with Traditional XML
Extensible Markup Language (XML) has been around longer than the original public release of the Microsoft .NET Framework 1.0. The Document Object Model (DOM) has long been used to manage in-memory representations of XML documents along with XPath and XQuery. Anyone who has had the experience of using it to create a large or complex XML representation of data can commiserate with the overly complex nature of creating and appending combinations of elements and attributes to an XML document over and over again. Just as complex is searching or querying an XML document for a particular element or attribute value. In my opinion, it has always been much more complex than it really needs to be. The following sample code demonstrates a creation of a simple XML document using DOM:
XmlDocument document = new XmlDocument(); XmlElement customer = document.CreateElement("Customer"); XmlElement firstName = document.CreateElement("FirstName"); XmlElement lastName = document.CreateElement("LastName"); firstName.InnerText = "Mark"; lastName.InnerText = "Strawmyer"; customer.AppendChild(firstName); customer.AppendChild(lastName); document.AppendChild(customer);
The preceding code is a very simple example, and it also produces only a very simple XML document. Anything with a large number of nodes requires an awful lot of object creation and repetitive code structure. Searching the XML for elements such as FirstName is an additional set of code.
Introducing LINQ to XML
LINQ and its ability to provide a unified data access model is just the solution to eliminate many challenges with traditional XML. LINQ to XML uses LINQ extension methods to read, create, search, and generally deal with XML in a simplified manner.
LINQ to XML Helper Objects
The previous pattern involved in creating XML is to create a node, set a value, and append the child. This involves multiple lines of code. LINQ to XML introduces a number of helper objects in the System.Xml.Linq namespace designed to allow developers to build and manage XML contents through parameter arrays and other functions that allow for an XML statement to be created in a single line of code. The following list of helper objects have been made available:
- XDocument: Represents an XML document instance
- XNamespace: Incorporates prefixes by allowing for XNamespace and a string to be used for a name
- XComment: Inserts a comment
- XElement: Represents an XML element that can be used as the container of any XML fragment
- XAttribute: Represents an XML attribute
The following sample demonstrates a sample of XML functional construction. Notice how it can be generated in a single line of code by using nested constructors as opposed to the traditional DOM approach that involves creating a number of objects on multiple lines of code.
XDocument customer = new XDocument( new XElement("Customer", new XElement("FirstName", "Mark"), new XElement("LastName", "Strawmyer")));
Test Driving LINQ to XML through Examples
Now that you've covered the background, you can explore a couple of examples of LINQ to XML. The examples will demonstrate the use of the helper objects along with an example of querying a sample XML document.
Creating XML from a Database
For this example, I downloaded the Northwind sample database and loaded it in my SQL Server so that I could retrieve and test data from it. I added a LINQ to SQL data class (which you'll look at closer in the next article) connected to the Northwind database. The following example code uses LINQ syntax to query data from the database and then uses LINQ to XML helper objects to generate and save a new XML document. As you can see, you don't have to use LINQ to XML to use the helper objects.
NorthwindDataContext db = new NorthwindDataContext(); XElement xe = new XElement("customers", from c in db.Customers where c.CompanyName.StartsWith("Split") select new XElement("customer", new XAttribute("Company", c.CompanyName), new XAttribute("Name", c.ContactName), new XAttribute("Title", c.ContactTitle), new XAttribute("City", c.City))); xe.Save("customers.xml");
The following XML snippet represents the output from the preceding example based on the contents of the Northwind database sample:
<?xml version="1.0" encoding="utf-8" ?> <customers> <customer Company="Split Rail Beer & Ale" Name="Art Braunschweiger" Title="Sales Manager" City="Lander" /> </customers>
Querying XML by Using LINQ to XML
In this example, you'll load an example XML data file that contains information on real estate listings. In this case, I have an XML file from what used to be expo.live.com that will be used. I've abbreviated the sample file to a single entry so that it fits into the article text and saved it in a file called exporss.xml.
<?xml version="1.0" encoding="utf-8"?> <rss version="2.0" xmlns:classifieds="http://expo.live.com/ns/2006/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"> <channel> <title>Expo RSS Feed</title> <link>http://expo.live.com/</link> <description>Expo RSS feed</description> <language>en-US</language> <pubDate>Wed, 25 Jul 2007 15:46:25 GMT</pubDate> <classifieds:totalListings>2384</classifieds:totalListings> <item> <title> Home for Rent early August: 4 bedrooms—2.5 baths </title> <link> http://expo.live.com/ViewListing.aspx?lid=5870094 </link> <description> Light and bright home that says "Welcome Home". </description> <pubDate>Wed, 25 Jul 2007 05:20:00 GMT</pubDate> <classifieds:listingid>5870094</classifieds:listingid> <classifieds:expirationDate> Wed, 19 Sep 2007 05:15:00 GMT </classifieds:expirationDate> <classifieds:currency>USD</classifieds:currency> <classifieds:price>2200.00</classifieds:price> <classifieds:category classifieds:id="18" classifieds:name="Houses" classifieds:transactionType="For Rent"> </classifieds:category> <classifieds:postedBy>Joannede</classifieds:postedBy> <classifieds:location> <classifieds:address></classifieds:address> <classifieds:city>Redmond</classifieds:city> <classifieds:state>WA</classifieds:state> <classifieds:country>US</classifieds:country> <classifieds:postcode>98052</classifieds:postcode> <classifieds:latitude> 47.6870625585192 </classifieds:latitude> <classifieds:longitude> -122.118400170762 </classifieds:longitude> </classifieds:location> <geo:lat>47.6870625585192</geo:lat> <geo:long>-122.118400170762</geo:long> <classifieds:details> <classifieds:NUMBER_BEDROOMS> 4 </classifieds:NUMBER_BEDROOMS> <classifieds:NUMBER_BATHROOMS> 2.5 </classifieds:NUMBER_BATHROOMS> <classifieds:YEAR_BUILT></classifieds:YEAR_BUILT> <classifieds:LOT_SIZE>2500</classifieds:LOT_SIZE> </classifieds:details> </item> </channel> </rss>