4. Reading an XML Document
The XDocument makes it easy to read and navigate XML
content. You can use the shared XDocument.Load() method to read XML
documents from a file, URI, or stream, and you can use the shared
XDocument.Parse() method to load XML content from a string.
Once you have the XDocument with your content in
memory, you can dig into the tree of nodes using a few key properties
and methods of the XElement and XDocument class. Table 1 lists the most useful methods.
Table 1. Useful Methods for XElement and XDocument
Method | Description |
---|
Attributes() | Gets the collection of XAttribute objects for this element. |
Attribute() | Gets the XAttribute with the specific name. |
Elements() | Gets
the collection of XElement objects that are contained by this element.
(This is the top level only—these elements may in turn contain more
elements.) Optionally, you can specify an element name, and only those
elements will be retrieved. |
Element() | Gets
the single XElement contained by this element that has a specific name
(or null if there's no match). If there is more than one matching
element, this method gets just the first one. |
Descendants() | Gets
the collection of XElement objects that are contained by this element
and (optionally) have the name you specify. Unlike the Elements()
method, this method goes through all the layers of the document and
finds elements at any level of the hierarchy. |
Nodes() | Gets
all the XNode objects contained by this element. This includes elements
and other content, such as comments. However, unlike the XmlTextReader
class, the XDocument does not consider attributes to be nodes. |
DescendantNodes() | Gets
all the XNode object contained by this element. This method is like
Descendants() in that it drills down through all the layers of nested
elements. |
These methods give you added flexibility to filter
out just the elements that interest you. For example, when using the
Elements() method, you have two overloads to choose from. You can get
all the child elements (in which case you would supply no parameters) or
get just those child elements that have a specific element name (in
which case you would specify the element name as a string). For example,
here's how you would get the root <SuperProProductList> element
from an XDocument that contains the complete SuperProProductList.xml:
' Use the Element() method, because there is just one matching element.
Dim superProProductListElement As XElement = doc.Element("SuperProProductList")
You can then use this element as a starting point to
dig deeper into the document. For example, if you want to find the child
<Product> elements in the <SuperProProductList>, you would
add this code:
' Use the Elements() method, because there are several matching elements.
Dim productElements = superProProductListElement.Elements("Product")
When defining the productElements object, this code
does not specify a type. This shortcut works because the VB compiler can
identify the correct type and fill it in automatically. (Technically,
the Elements() method returns a strongly typed IEnumerable(Of XElement)
collection, which is a generic collection that's customized to work with
XElement objects.)
Getting the text inside an XElement is easy. In fact,
Visual Studio is intelligent enough to automatically convert an
XElement to the data type you want, as shown here:
Dim priceElement As XElement = productElement.Element("Price")
Dim price As Decimal = priceElement
This works because the XElement class defines
specialized conversion operators. When you assign an XElement to a
decimal variable, for example, the XElement automatically retrieves its
inner value and attempts to convert that to a decimal.
If you have the Option Strict compilation set, which
requires you to perform explicit data type conversion, you can use the
same approach, but you need to use a conversion function like CType to
make your intent clear to the VB compiler:
Dim priceElement As XElement = productElement.Element("Price")
Dim price As Decimal = CType(priceElement, Decimal)
Setting the text content inside an element is nearly
as easy. You simply assign the new content to the Value property, as
shown here:
priceElement.Value = CType(priceElement, Decimal) * 2;
You can use the same approach to read and set attributes with the XAttribute class.
Here's a straightforward code routine that mimics the
XML processing code you saw earlier with the XmlTextReader. It scans
through the elements that are available, creates a list of products, and
displays that in a grid.
' Load the document.
Dim doc As XDocument = XDocument.Load(file)
' Loop through all the nodes, and create the list of Product objects .
Dim products As List(Of Product) = New List(Of Product)()
For Each element As XElement In _
doc.Element("SuperProProductList").Elements("Product")
Dim newProduct As New Product()
newProduct.ID = element.Attribute("ID")
newProduct.Name = element.Attribute("Name")
newProduct.Price = element.Element("Price")
products.Add(newProduct)
Next
' Display the results.
gridResults.DataSource = products
gridResults.DataBind()
' Display the retrieved document.
gridResults.DataSource = Products
gridResults.DataBind()
The XElement class offers quite a few more members.
For example, you'll find members for quickly stepping from one node to
the next (FirstNode, LastNode, NextNode, PreviousNode, and Parent),
properties for testing for the presence of children (HasElements),
attributes (HasAttributes), content (IsEmpty), and methods for
inserting, removing, and otherwise manipulating the XML tree of nodes.
For example, use Add() to place a new child element inside the current
element (after any existing content); use AddFirst() to place a new
child element inside the current element (before any existing content);
use AddAfterSelf() to insert an element at the same level just after the
current element; use AddBeforeSelf() to insert an element at the same
level just before the current element; and so on. You can also use
Remove(), RemoveNodes(), ReplaceWith(), and ReplaceNodes() to remove or
replace elements and other nodes.
The following example shows how you can add a new product to the XDocument:
' Create the element for the new product.
Dim newProduct As New XElement("Product", _
New XAttribute("ID", 4), _
New XAttribute("Name", "Magic Lantern"), _
New XElement("Price", "76.95"))
' Add the element to the end of the current product list.
doc.Element("SuperProProductList").Add(newProduct)
Whether you use the XDocument or the XmlTextReader
class depends on a number of factors. Generally, you use XDocument when
you want to deal directly with XML, rather than just using XML as a way
to persist some information. It also gives you the ability to modify the
structure of an XML document, and it allows you to browse XML
information in a more flexible way (not just from start to finish). On
the other hand, the XmlTextReader is best when dealing with large XML
files, because it won't attempt to load the entire document into memory
at once.
|
|
5. Searching an XML Document
One of the nicest features of the XDocument is its
support of searching, which allows you to find nodes when you know they
are there—somewhere—but you aren't sure how many matches exist or where
the elements are.
To search an XDocument, all you need to do is use the
Descendants() or DescendantNodes() method. Both methods allow you to
search through the entire document tree in one step. For example, here's
how you can use Descendants() on the entire SuperProProductList.xml
document to get a list of prices:
Dim doc As XDocument = XDocument.Load(file)
' Find the matches.
Dim results = doc.Descendants("Price")
' Display the results.
lblXml.Text = "<b>Found " & results.Count(Of XElement)().ToString() & " Matches "
lblXml.Text &= " for the Price tag: </b><br /><br />"
For Each result As XElement In results
lblXml.Text += result.Value & "<br />"
Next
Figure 6 shows the result.
The Descendants() method is
great if you want to find an element based on its name. If you want to
use more sophisticated searching, match only part of a name, or examine
only part of a document, you have two choices. First, you can to write
code that loops through all the nodes in the XDocument and checks each
one. Second, you can use the LINQ to XML feature to perform a query that
extracts matching XElement objects from your XDocument. This is a
natural fit, because the XDocument class was originally introduced as
part of the LINQ feature in .NET 3.5.