An Extensive Examination of LINQ: Introducing LINQ to XMLBy Scott Mitchell
XML is an increasingly popular way to encode documents, data, and electronic messages. There are a number of ways to programmatically create, modify, and search XML files. Since its inception, the .NET Framework's
System.Xmlnamespace has included classes for programmatically working with XML documents. For instance, the
XmlWriterclasses offer developers a means to read from or write to XML files in a fast, forward-only manner, while the
XmlDocumentclass allows developers to work with an XML document as an in-memory tree representation.
LINQ to XML is a new set of XML-related classes in the .NET Framework (found in the
System.Xml.Linq namespace), which enable developers to work with XML documents using
LINQ's features, syntax, and semantics. Compared to .NET's existing XML APIs, LINQ to XML is a simpler, easier to use API. For a given task, LINQ to XML code is typically shorter
and more readable than code that uses the
XmlWriter classes. And perhaps most importantly, LINQ to XML allows
you to leverage your existing knowledge and familiarity with LINQ's standard query operators and query syntax.
This article is the first in a series of articles that examines LINQ to XML. This installment introduces the LINQ to XML API, examines some of the more pertinent classes
System.Xml.Linq namespace, and shows how to perform a number of common XML tasks using the API. Read on to learn more!
XElement - The LINQ to XML Workhorse
The most frequently used class in the LINQ to XML API is the
XElementclass, which represents an XML element. This class is used when programmatically constructing an XML document, when loading an XML document, and when searching, filtering, or otherwise enumerating the elements within an XML document.
For example, the
XElement class has a static
which loads an XML document and returns the root of the just-loaded document as an
XElement object. The
Load method can be passed in the path to
an XML file on disk or the URL of an XML file online. You can also pass in a
The demo available for download at the end of this article includes an XML file in the
App_Data folder named
Inventory.xml. This XML file has a
root element named
<inventory> and then a variable number of
<item> elements. Each
<item> element represents an item
in inventory and contains children elements like
<quantity>, among others. A snippet of this
XML file is shown below.
The following code loads this XML file into an
XElement object. Keep in mind that we need to pass in the full path to the XML file to load
C:MySitesLINQDemosApp_DataInventory.xml). To compute this path I use the
Server.MapPath method, which converts a virtual path (
into a physical path. For more information, see Using
XElement class includes a variety of properties and methods for working with the element's attributes and for working with the element's text value. There are
also methods for getting ancestor and descendant elements. The
property returns the concatenated text contents of the element and the text content of its descendants. For example, the
Value property of the
variable created in the above code snippet returns a string of all of the text content in the
Inventory.xml file (since
xml represents the root of the
Note that all of the text is concatenated. I've used varying colors to help disambiguate where one text element ends and the next begins.
Two other helpful members of the
XElement class are
Elements method returns all of the
child elements of the current element. You can optionally pass in an element name and then only those children element with a matching name are returned. The
Element method requires a name as an input parameter and then returns the first child element with that name.
The following snippet shows these two methods in action. The example starts by enumerating through the children elements of the root element (namely, visiting each
<item> element. For each
<item> element the
<unitPrice> elements are grabbed via the
Element method and their text values are read into a variable via their
Understand that the
Elements method returns a collection of
XElement objects as type
IEnumerable<XElement>. As we've discussed
throughout this article series, LINQ is designed to work with collections of data of type
IEnumerable<T>. Consequently, we can use LINQ's standard
query operators and C# and Visual Basic's query syntax when working with LINQ to XML. The above loop can be rewritten using the query
syntax. The following demo shows using the query syntax to bind the name and price of each inventory item to a BulletedList control. The ASP.NET page contains a
BulletedList control named
DataTextValue property is set to "NameAndPrice". The query syntax code enumerates through each
<item> element (via
xml.Elements()) and for each
<item> element it returns an object with a single string property named
NameAndPrice that is assigned the value "Name (Price)".
The screen shot below shows the BulletedList control when viewed through a browser.
XElementclass can also be used to create XML content. One of the
XElementconstructor overloads accepts as input a name for the element and an array of objects that defines the element's content. This constructor can be used in a terse and human-readable format to programmatically generate XML content. For instance, the following snippet of C# code programmatically creates XML content whose structure mirrors that of the
The above code is both terse and readable. You can determine the structure and contents of the XML being created by browsing the code. And things get even cooler if you are using Visual Basic, which supports XML literals. While you can certainly create XML content in VB using virtually identical syntax to that shown above, VB's XML literals allow for even more readable code. The following snippet creates the same XML content, but uses XML literals:
Modifying XML Content
SetElementValuemethods are used to modifying attribute or text values for an element. For example, the following code snippet doubles the price of every item in the
xml is the root element of the
Inventory.xml file. The above code starts by enumerating all of the root element's children via a
loop. For each child, we get the
<unitPrice> value and then double it via a call to
SetElementValue. When this loop completes, each item in
the inventory will have had its
<unitPrice> value doubled.
Keep in mind that in the code snippets above any XML content created or modified via the
XElementclass's methods are in-memory changes. In other words, in the preceding example where we doubled each price, that code does not actually modify the contents of the
Inventory.xmlfile. In other words, when we load XML content via a call to
XElement.Load(pathToXml), what's happening is that the XML content is being read from disk into an
XElementobject in memory. Any changes to that
XElementobject - such as doubling the price - is a change made to that
XElementobject in memory. The change is not persisted back to the
Inventory.xmlfile unless we explicitly save the XML content back to disk.
To save XML content to disk use the
You can pass this method a
TextWriter object, an
XmlWriter object, or a string that specifies the file path to where the content should be written.
The following snippet saves the contents of an
XElement to a file named
XMLOutput.xml in the same folder as the ASP.NET page that made this call.
Returning to the double prices example, to save the doubled prices back to the
Inventory.xml file you could use C# code similar to the following.
(Download the demo available at the end of this article to see this code snippet in Visual Basic.)
The demo available for download includes a web page named
ModifyXML.aspx that allows users to double or halve prices with the click of a button. The screen show
below shows the page's output immediately after the user has clicked the "Double Prices" button.
This article focused on the
XElementobject and techniques for creating, loading, and modifying XML content. We've only scratched the surface of the LINQ to XML API, as we've yet to look at how to add and remove XML elements, how to deal with namespaces, how to do XML transforms, and how to perform standard LINQ operations against XML, such as projections, filtering, aggregating, and so forth. These topics and more will be covered in future articles in this series.
Until then... Happy Programming!