LINQ to XML

The previous sections of this chapter provided you with a general understanding of XML, how it is structured and how it can be accessed and manipulated using the .NET Framework. This section covers working with XML using newer features, specifically Language Integrated Query (LINQ), added in version 3.5 of the framework.

With the introduction of LINQ to the .NET Framework, the focus was on easy access to the data that you want to work with in your applications. One of the main data stores in the application space is XML, so it was really a no-brainer to create the LINQ to XML implementation. With the inclusion of System.Xml.Linq, you now have a series of capabilities that make the process of working with XML in your code much easier to achieve.

This chapter will only touch on how LINQ is used in relationship to XML. For the more basic description of LINQ and how it is used or works in a general sense, refer to chapter 9.

LINQ Helper XML Objects

Even if the LINQ querying capability were not around, the new objects available to work with the XML are so good that they can even stand on their own outside LINQ. Within the new System.Xml.Linq namespace, you will find a series of new LINQ to XML helper objects that make working with an XML document in memory that much easier. The following sections describe the new objects that are available within this new namespace.


Note
Many of the examples in this chapter use a file called Hamlet.xml, which you can find included in the file http://metalab.unc.edu/bosak/xml/eg/shaks200.zip. At this link you'll find all of Shakespeare's plays as XML files.

XDocument

The XDocument class is a replacement of the XmlDocument object from the pre-LINQ world. While it does not comply with any international standards, the XDocument object is easier to work with when dealing with XML documents. It works with the other new objects in this space, such as the XNamespace, XComment, XElement, and XAttribute objects.

The LinqRead project provides an example that demonstrates the use of the XDocument class. This project is covered in more detail a little later. One of the more important members of the XDocument object is the Load method:

Dim xdoc As XDocument = XDocument.Load("C:Hamlet.xml")

The preceding example loads the Hamlet.xml contents as an in-memory XDocument object. You can also pass a TextReader or an XmlReader object into the Load method. From here, you can programmatically work with the XML (code file: Main.vb):

Dim xdoc As XDocument = XDocument.Load("C:Hamlet.xml")
Console.WriteLine(xdoc.Root.Name.ToString())
Console.WriteLine(xdoc.Root.HasAttributes.ToString())

This produces the following results:

PLAY
False

Another important member to be aware of is the Save method, which enables you to save to a physical disk location or to a TextWriter or an XmlWriter object:

Dim xdoc As XDocument = XDocument.Load("Hamlet.xml")
xdoc.Save("CopyOfHamlet.xml")

XElement

Another common object that you will work with is the XElement object. With this object, you can easily create even single-element objects that are XML documents themselves, and even fragments of XML. For instance, here is an example of writing an XML element with a corresponding value:

Dim xe As XElement = New XElement("Company", "Wrox")
Console.WriteLine(xe.ToString())

When creating a new XElement object, you can define the name of the element as well as the value used in the element. In this case, the name of the element will be <Company>, while the value of the <Company> element will be Wrox. Running this in a console application, you will get the following result:

<Company>Wrox</Company>

The XElementWriting project provides an example that demonstrates how you can also create a more complete XML document using multiple XElement objects, as shown here (code file: Main.vb):

Imports System.Xml.Linq
         
Module Main
         
    Sub Main()
         
        Dim root As New XElement("Company",
                                 New XAttribute("Type", "Publisher"),
                                 New XElement("CompanyName", "Wrox"),
                                 New XElement("CompanyAddress",
                                     New XElement("Street", "111 River Street"),
                                     New XElement("City", "Hoboken"),
                                     New XElement("State", "NJ"),
                                     New XElement("Country", "USA"),
                                     New XElement("Zip", "07030-5774")))
        Console.WriteLine(root.ToString())
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
         
End Module

Running this application yields the results shown in Figure 8.1.

Figure 8.1 Output for the XElementWriting example

8.1

XNamespace

The XNamespace is an object that represents an XML namespace, and it is easily applied to elements within your document. An example of this can be found in the XElementWritingNamespaces project. It is a variation of the previous example with only minor edits to include a namespace for the root element, as seen here:

Imports System.Xml.Linq
         
Module Main
         
    Sub Main()
        Dim ns as Xnamespace = "http://www.example.com/somenamespace"
        Dim root As New Xelement(ns + "Company",
                                 New XElement("CompanyName", "Wrox"),
                                 New XElement("CompanyAddress",
                                     New XElement("Street", "111 River Street"),
                                     New XElement("City", "Hoboken"),
                                     New XElement("State", "NJ"),
                                     New XElement("Country", "USA"),
                                     New XElement("Zip", "07030-5774")))
        Console.WriteLine(root.ToString())
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
         
End Module

In this case, an XNamespace object is created by assigning it a value of http://http://www.example.com/somenamespace. From there, it is actually used in the root element <Company> with the instantiation of the XElement object:

Dim root As New XElement(ns + "Company",

This will produce the results shown in Figure 8.2.

Figure 8.2 Output of XElementWriting using a namespace

8.2

Besides dealing with the root element, you can also apply namespaces to all your elements (code file: Main.vb):

Imports System.Xml.Linq
         
Module Main
         
    Sub Main()
        Dim ns1 As XNamespace = "http://www.example.com/ns/root"
        Dim ns2 As XNamespace = "http://www.example.com/ns/address"
         
        Dim root As New XElement(ns1 + "Company",
                                 New XElement(ns1 + "CompanyName", "Wrox"),
                                 New XElement(ns2 + "CompanyAddress",
                                     New XElement(ns2 + "Street", 
                                                  "111 River Street"),
                                     New XElement(ns2 + "City", "Hoboken"),
                                     New XElement(ns2 + "State", "NJ"),
                                     New XElement(ns2 + "Country", "USA"),
                                     New XElement(ns2 + "Zip", "07030-5774")))
        Console.WriteLine(root.ToString())
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
         
End Module

This produces the results shown in Figure 8.3.

Figure 8.3 Output for the XElementWritingNamespaces example

8.3

Since the namespace was applied to the <CompanyAddress>, all of its child elements (<Street>, <City>, <State>, <Country>, and <Zip>) also have this same namespace, since elements inherit the namespace of their parent.

XAttribute

In addition to elements, another important aspect of XML is attributes, as mentioned earlier in this chapter. Adding and working with attributes is done through the use of the XAttribute object. The following example adds an attribute to the root <Company> node:

        Dim root As New Xelement("Company",
                             New Xattribute("Type", "Publisher"),
                                 New XElement("CompanyName", "Wrox"),
                                 New XElement("CompanyAddress",
                                     New XElement("Street", "111 River Street"),
                                     New XElement("City", "Hoboken"),
                                     New XElement("State", "NJ"),
                                     New XElement("Country", "USA"),
                                     New XElement("Zip", "07030-5774")))

Here, the attribute MyAttribute with a value of MyAttributeValue is added to the root element of the XML document, producing the results shown in Figure 8.4.

Figure 8.4 Output of XElementWriting with added attribute

8.4

XML Literals

LINQ provides a great feature, called XML literals, that can be used to greatly simplify working with XML. Using XML literals, you can place XML directly in your code for working with the XDocument and XElement objects. This works due to the fact that the literal XML is converted directly to appropriate objects, such as XElement and XAttribute.

Earlier, in the XElementWriting example, the use of the XElement object was presented as follows:

Imports System.Xml.Linq
         
Module Main
         
    Sub Main()
        Dim root As New XElement("Company",
                                 New XElement("CompanyName", "Wrox"),
                                 New XElement("CompanyAddress",
                                     New XElement("Street", "111 River Street"),
                                     New XElement("City", "Hoboken"),
                                     New XElement("State", "NJ"),
                                     New XElement("Country", "USA"),
                                     New XElement("Zip", "07030-5774")))
        Console.WriteLine(root.ToString())
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
         
End Module

The XmlLiterals project instead uses XML literals to perform the same functionality, seen here (code file: Main.vb):

Module Main
         
    Sub Main()
        Dim root As XElement =
            <Company>
                <CompanyName>Wrox</CompanyName>
                <CompanyAddress>
                    <Street>111 River Street</Street>
                    <City>Hoboken</City>
                    <State>NJ</State>
                    <Country>USA</Country>
                    <Zip>07030-5774</Zip>
                </CompanyAddress>
            </Company>
        Console.WriteLine(root.ToString())
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
         
End Module

This enables you to place the XML directly in the code. The best part about this is the IDE support for XML literals. Visual Studio 2012 has IntelliSense and excellent color-coding for the XML that you place in your code file. As Figure 8.5 shows, there is no difference in the output between this example and the previous one, which didn't use XML literals.

Figure 8.5 Output of XElementWriting using XML literals

8.5

You can also use inline variables in the XML document. For instance, if you wanted to declare the value of the <CompanyName> element outside the XML literal, then you could use a construct similar to the following:

Module Module1
    Sub Main()
        Dim companyName As String = "Wrox"
        Dim xe As XElement = _
                <Company>
                <CompanyName><%= companyName %></CompanyName>
                    <CompanyAddress>
                    <Street>111 River Street</Street>
                    <City>Hoboken</City>
                    <State>NJ</State>
                    <Country>USA</Country>
                    <Zip>07030-5774</Zip>
                </CompanyAddress>
            </Company>
        Console.WriteLine(xe.ToString())
        Console.ReadLine()
    End Sub
End Module

In this case, the <CompanyName> element is assigned a value of Wrox from the companyName variable, using the syntax <%= companyName %>.

Querying XML Documents

As mentioned in the beginning of this section, and in other chapters, LINQ stands for Language Integrated Query. The primary purpose for its existence is to provide a streamlined approach to querying data.

Now that you can get your XML documents into an XDocument object and work with the various parts of this document, you can also use LINQ to XML to query your XML documents and work with the results.

Static XML Documents

The functionality provided by LINQ makes querying a static XML document take almost no work at all. The following example, from the LinqRead project, makes use of the hamlet.xml file. The example demonstrates querying for all the players (actors) who appear in a play. Each of these players is defined in the XML document with the <PERSONA> element (code file: Main.vb):

Module Main
    Sub Main()
        Dim xdoc As XDocument = XDocument.Load("C:hamlet.xml")
        Dim query = From people In xdoc.Descendants("PERSONA") _
                    Select people.Value
        Console.WriteLine("{0} Players Found", query.Count())
        Console.WriteLine()
        For Each item In query
            Console.WriteLine(item)
        Next
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
End Module

In this case, an XDocument object loads a physical XML file (hamlet.xml) and then performs a LINQ query over the contents of the document:

Dim query = From people In xdoc.Descendants("PERSONA") _
            Select people.Value

The people object is a representation of all the <PERSONA> elements found in the document. Then the Select statement gets at the values of these elements. From there, a Console.WriteLine method is used to write out a count of all the players found, using query.Count. Next, each of the items is written to the screen in a For Each loop. The results you should see are presented here:

26 Players Found
CLAUDIUS, king of Denmark.
HAMLET, son to the late, and nephew to the present king.
POLONIUS, lord chamberlain.
HORATIO, friend to Hamlet.
LAERTES, son to Polonius.
LUCIANUS, nephew to the king.
VOLTIMAND
CORNELIUS
ROSENCRANTZ
GUILDENSTERN
OSRIC
A Gentleman.
A Priest.
MARCELLUS
BERNARDO
FRANCISCO, a soldier.
REYNALDO, servant to Polonius.
Players.
Two Clowns, grave-diggers.
FORTINBRAS, prince of Norway.
A Captain.
English Ambassadors.
GERTRUDE, queen of Denmark, and mother to Hamlet.
OPHELIA, daughter to Polonius.
Lords, Ladies, Officers, Soldiers, Sailors, Messengers, and other Attendants.
Ghost of Hamlet's Father.

Dynamic XML Documents

Numerous dynamic XML documents can be found on the Internet these days. Blog feeds, podcast feeds, and more provide XML documents by sending a request to a specific URL endpoint. These feeds can be viewed either in the browser, through an RSS aggregator, or as pure XML. The LinqReadDynamic project includes an example to demonstrate reading and querying an RSS feed. The code to do this is (code file: Main.vb):

Module Module1
    Sub Main()
        Dim xdoc As XDocument = _
                       XDocument.Load("http://weblogs.asp.net/mainfeed.aspx")
        Dim query = From rssFeed In xdoc.Descendants("channel") _
                    Select Title = rssFeed.Element("title").Value, _
                       Description = rssFeed.Element("description").Value, _
                       Link = rssFeed.Element("link").Value
        For Each item In query
            Console.WriteLine("TITLE: " + item.Title)
            Console.WriteLine("DESCRIPTION: " + item.Description)
            Console.WriteLine("LINK: " + item.Link)
        Next
        Console.WriteLine()
        Dim queryPosts = From myPosts In xdoc.Descendants("item") _
                Select Title = myPosts.Element("title").Value, _
                       Published = _
                         DateTime.Parse(myPosts.Element("pubDate").Value), _
                       Description = myPosts.Element("description").Value, _
                       Url = myPosts.Element("link").Value
        For Each item In queryPosts
            Console.WriteLine(item.Title)
        Next
        Console.WriteLine("Press ENTER to exit")
        Console.ReadLine()
    End Sub
End Module

Here, the Load method of the XDocument object points to a URL where the XML is retrieved. The first query pulls out all the main subelements of the <channel> element in the feed and creates new objects called Title, Description, and Link to get at the values of these subelements.

From there, a For Each statement is run to iterate through all the items found in this query. The second query works through all the <item> elements and the various subelements it contains (these are all the blog entries found in the blog). Though a lot of the items found are rolled up into properties, in the For Each loop, only the Title property is used. You will see results similar to that shown in Figure 8.6.

Figure 8.6 Output for the LinqReadDynamic example

8.6

Reading and writing XML Documents

If you have been working with the XML document hamlet.xml, you probably noticed that it is quite large. You've seen how you can query into the XML document in a couple of ways, and now this section takes a look at reading and writing to the XML document.

Reading from an XML Document

Earlier you saw just how easy it is to query into an XML document using the LINQ query statements, as shown here:

Dim query = From people In xdoc.Descendants("PERSONA") _
            Select people.Value

This query returns all the players found in the document. Using the Element method of the XDocument object, you can also get at specific values of the XML document you are working with. For instance, continuing to work with the hamlet.xml document, the following XML fragment shows you how the title is represented:

<?xml version="1.0"?>
<PLAY>
   <TITLE>The Tragedy of Hamlet, Prince of Denmark</TITLE>
   <!-- XML removed for clarity -->
</PLAY>

As you can see, the <TITLE> element is a nested element of the <PLAY> element. You can easily get at the title by using the following bit of code:

Dim xdoc As XDocument = XDocument.Load("hamlet.xml")
Console.WriteLine(xdoc.Element("PLAY").Element("TITLE").Value)

This bit of code writes out the title “The Tragedy of Hamlet, Prince of Denmark” to the console screen. In the code, you were able to work down the hierarchy of the XML document by using two Element method calls—first calling the <PLAY> element, and then the <TITLE> element found nested within the <PLAY> element.

Continuing with the hamlet.xml document, you can view a long list of players who are defined with the use of the <PERSONA> element:

<?xml version="1.0"?>
<PLAY>
   <TITLE>The Tragedy of Hamlet, Prince of Denmark</TITLE>
   <!-- XML removed for clarity -->
   <PERSONAE>
      <TITLE>Dramatis Personae</TITLE>
      <PERSONA>CLAUDIUS, king of Denmark. </PERSONA>
      <PERSONA>HAMLET, son to the late,
       and nephew to the present king.</PERSONA>
      <PERSONA>POLONIUS, lord chamberlain. </PERSONA>
      <PERSONA>HORATIO, friend to Hamlet.</PERSONA>
      <PERSONA>LAERTES, son to Polonius.</PERSONA>
      <PERSONA>LUCIANUS, nephew to the king.</PERSONA>
      <!-- XML removed for clarity -->
   </PERSONAE>
</PLAY>

Using that, review the following bit of the code's use of this XML:

Dim xdoc As XDocument = XDocument.Load("hamlet.xml")
Console.WriteLine( _
   xdoc.Element("PLAY").Element("PERSONAE").Element("PERSONA").Value)

This piece of code starts at <PLAY>, works down to the <PERSONAE> element, and then makes use of the <PERSONA> element. However, using this you will get the following result:

CLAUDIUS, king of Denmark

Although there is a collection of <PERSONA> elements, you are dealing only with the first one that is encountered using the Element().Value call.

Writing to an XML Document

In addition to reading from an XML document, you can also write to the document just as easily. The LinqWrite project demonstrates this by providing an example that allows you to change the name of the first player of the hamlet file. The code to accomplish this is (code file: Main.vb):

Module Module1
    Sub Main()
        Dim xdoc As XDocument = XDocument.Load("hamlet.xml")
        xdoc.Element("PLAY").Element("PERSONAE"). _
           Element("PERSONA").SetValue("Foo deBar, King of Denmark")
        Console.WriteLine(xdoc.Element("PLAY"). _
           Element("PERSONAE").Element("PERSONA").Value)
        Console.ReadLine()
    End Sub
End Module

In this case, the first instance of the <PERSONA> element is overwritten with the value of Foo deBar, King of Denmark using the SetValue method of the Element object. After the SetValue is called and the value is applied to the XML document, the value is then retrieved using the same approach as before. Running this bit of code, you can indeed see that the value of the first <PERSONA> element has been changed.

Another way to change the document is shown in the LinqAddElement project. This example creates the elements you want as XElement objects and then adds them to the document, shown here (code file: Main.vb):

Module Module1
    Sub Main()
        Dim xdoc As XDocument = XDocument.Load("hamlet.xml")
        Dim xe As XElement = New XElement("PERSONA", _
                 "Foo deBar, King of Denmark")
        xdoc.Element("PLAY").Element("PERSONAE").Add(xe)
        Dim query = From people In xdoc.Descendants("PERSONA") _
                    Select people.Value
        Console.WriteLine("{0} Players Found", query.Count())
        Console.WriteLine()
        For Each item In query
            Console.WriteLine(item)
        Next
        Console.ReadLine()
    End Sub
End Module

In this case, an XElement document called xe is created. The construction of xe gives you the following XML output:

<PERSONA>Foo deBar, King of Denmark</PERSONA>

Then, using the Element().Add method from the XDocument object, you are able to add the created element:

xdoc.Element("PLAY").Element("PERSONAE").Add(xe)

Next, querying all the players, you will now find that instead of 26, as before, you now have 27, with the new one at the bottom of the list. Besides Add, you can also use AddFirst, which does just that—adds the player to the beginning of the list instead of the end, which is the default.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset