3. Working with XML Documents in Memory
The XmlTextReader and XmlTextWriter use XML as a backing store.
These classes are streamlined for quickly getting XML data into and out
of a file (or some other source). When using these classes, you open
your XML file, retrieve the data you need, and use that data to create
the appropriate objects or fill the appropriate controls. Your goal is
to translate the XML into something
more practical and usable. The rest of your code has no way of knowing
that the data was initially extracted from an XML document—and it
doesn't care.
NOTE
Remember, the terms XML document and XML file are different. An XML document
is a collection of elements structured according to the rules of XML.
An XML document can be stored in virtually any way you want—it can be
placed in a file, in a field, or in a database, or it can simply exist
in memory.
The XDocument class provides a different approach to
XML data. It provides an in-memory model of an entire XML document. You
can then browse through the entire document, reading, inserting, or
removing nodes at any location. (You can find the XDocument and all
related classes in the System.Xml.Linq namespace.)
When using this approach, you begin by loading XML
content from a file (or some other source) into an XDocument object. The
XDocument holds the entire document at once, so it isn't a practical
approach if your XML content is several megabytes in size. (If you have a
huge XML document, the XmlTextReader and XmlTextWriter classes offer
the best approach.) However, the XDocument really excels with the
editing capabilities that it gives you. Using the XDocument object, you
can manipulate the content or structure of any part of the XML document.
When you're finished, you can save the content back to a file. Unlike
the XmlTextReader and XmlTextWriter, the XDocument class doesn't
maintain a direct connection to the file.
NOTE
The XDocument is .NET's most modern tool for
in-memory XML manipulation. It was introduced as part of LINQ ,
in .NET 3.5. Before this, .NET developers used a similar but slightly
more awkward class called XmlDocument. This distinction is important,
because the XmlDocument is still kicking around in .NET 4 and there are a
few old classes and methods that expect it, instead of the XDocument.
However, because the XDocument is more powerful and more convenient,
Microsoft recommends that you make it your go-to tool for serious XML
work.
When you use the XDocument class, your XML document is created as a series of linked .NET objects in memory. Figure 4
shows the object model. (The diagram is slightly simplified from what
you'll find when you start using the XDocument class—namely, it doesn't
show the attributes, each of which is represented by an XAttribute
object.)
To start building a next XML document, you need to
create the XDocument, XElement, and XAttribute objects that comprise it.
All these classes have useful constructors that allow you to create and
initialize them in one step. For example, you can create an element and
supply text content that should be placed inside using code like this:
Dim element As New XElement("Price", "23.99")
This is already better than the XmlTextWriter, which
forces you to start an element, insert its content, and close it with
three separate statements. But the code savings become even more
dramatic when you consider another feature of the XDocument and XElement
classes—their ability to create a nested tree of nodes in a single code
statement.
Here's how it works. Both the XDocument and XElement
class include a constructor that takes a parameter array for the last
argument. This parameter array holds a list of nested nodes.
NOTE
A parameter array
is a parameter that's preceded with the ParamArray keyword. This
parameter is always the last parameter, and it's always an array. The
advantage is that users don't need to declare the array—instead, they
can simply tack on as many arguments as they want, which are grouped
into a single array automatically.
Here's an example that creates an element with three nested elements and their content:
Dim element As New XElement("Product", _
New XElement("ID", 3), _
New XElement("Name", "Fresh Fruit Basket"), _
New XElement("Price", "49.99"))
Here's the scrap of XML that this code creates:
<Product>
<ID>3</ID>
<Name>Fresh Fruit Basket</Name>
<Price>49.99</Price>
</Product>
You can extend this technique to create an entire XML
document, complete with elements, text content, attributes, and
comments. For example, here's the complete code that creates the
SuperProProductList.xml document in memory. When the document is
completely built, the code saves it to a file using the XDocument.Save()
method.
' Build the XML content in memory.
Dim doc As New XDocument( _
New XDeclaration("1.0", Nothing, "yes"), _
New XComment("Created with the XDocument class."), _
New XElement("SuperProProductList", _
New XElement("Product", _
New XAttribute("ID", 1), _
New XAttribute("Name", "Chair"), _
New XElement("Price", "49.33")), _
New XElement("Product", _
New XAttribute("ID", 2), _
New XAttribute("Name", "Car"), _
New XElement("Price", "43399.55")), _
New XElement("Product", _
New XAttribute("ID", 3), _
New XAttribute("Name", "Fresh Fruit Basket"), _
New XElement("Price", "49.99"))))
' Save the document.
doc.Save(file)
This code creates the same XML content as the
XmlTextWriter code you considered earlier. However, this code is shorter
and easier to read.
3.1. Dissecting the Code . . .
Every separate part of the XML document is
created as an object. Elements are created as XElement objects, comments
are created as XComment objects, and attributes are represented as
XAttribute objects.
Unlike the code that uses the XmlTextWriter, there's no need to explicitly close elements.
Another
nice detail is the way the indenting of the code statements mirrors the
nesting of the elements in the XML document. If one element is followed
by another and both elements have the same indenting, then the two
elements are at the same level (for example, one <Product> element
after another). If one element is followed by another and the second
element has a greater indenting, it's being placed inside the preceding
element (for example, the <Price> element in the <Product>
element). The same holds true for other types of nodes, such as comments
and attributes. This indenting allows you to look at your code and
quickly take in the overall shape of the XML document.
One
of the best features of the XDocument class is that it doesn't rely on
any underlying file. When you use the Save() method, the file is
created, a stream is opened, the information is written, and the file is
closed, all in one line of code. This means this is probably the only
line you need to put inside a Try/Catch error handling block.
Figure 5 shows the file written by this code (as displayed by Internet Explorer).