"Loading and Persisting XML with an XML Document
Store Object"
This is the third in a series of three articles that look in
detail at how the new features within the System.Xml namespace in version 2.0 of
the .NET Framework can be used to read and write XML documents, and interact
with the new XML document store objects. The topics covered in the previous two
articles are:
- The new "settings" classes and static Create methods for XmlReader and XmlWriter
- Creating and using an XmlReader to read and validate XML
documents and fragments
- Some of the useful new features of the XmlReader class
- Using an XmlWriter to write XML documents and fragments
- Some of the useful new features of the XmlWriter class
In this final article, we look at how the updated XML
document store objects XmlDocument, XmlDataDocument and XPathDocument can be used - both stand-alone and in conjunction with the XmlReader and XmlWriter classes
- to read, persist and write XML documents and fragments more easily and more
efficiently than in version 1.x of .NET. The topics we'll be covering
are:
- A general overview of the XML document stores in System.Xml v
2.0
- Reading and Writing XML with the XPathDocument
- Reading and Writing XML with the XmlDocument
- Reading and Writing XML with the XmlDataDocument
As in the previous article, we'll look into the issues
involved in using the new classes, the reasoning behind the changes, and how
the new features simplify your code and provide better overall efficiency for
your applications.
The Version 2.0 XML Document Stores
In the previous two articles, you've seen
how the new XmlReader and XmlWriter classes are both powerful, and yet easy to use. They provide the best
performance when reading, streaming and writing XML documents where you don't
actually need to persist the XML content as a tree of nodes in memory. However,
there are times when it is necessary to load and be able to access a complete
XML document - for example when you want to perform random access to the
content, use XPath queries to locate fragments or individual elements (perhaps
to perform XST-T transformations on subsets of elements), or apply business
rules that require access to multiple sections of the document.
As in version 1.x, there are three
XML document objects in System.Xml and its subsidiary namespaces.
However, the updated XmlDocument class in particular gains many new features compared to the version
1.x equivalent. The three objects are:
- The XmlDocument class,
which is an XML parser and in-memory store that implements the W3C Level 2
XML DOM core functionality. An XPathNavigatorcan
be created over the document to provide a wide range of extra features that
allow easier access to the nodes and values, support the XML Infoset
model, and allow cursor-style navigation and updating of the content.
- The XmlDataDocument class, which is basically the same as the XmlDocument (it inherits from it), but has the additional capability to expose the XML content
as an ADO.NET DataSet instance - providing easy interaction between relational and
XML-based views of the data.
- The XPathDocument class
(in the System.Xml.XPath namespace), which is
a high-performance in-memory document store that is optimized for use with
XPath queries. It is read-only and does not support the W3C XML DOM
functionality, making it leaner and more efficient where these features
are not required. An XPathNavigator must be created over
the document to access the content.
The XmlDocument class is the
primary choice for tasks where an in-memory store is required, unless you can meet
all of your requirements using the XPathDocument class with
XPath queries. The XmlDataDocument should only be used where you need to access the contents as a DataSet.
Methods for Loading and Persisting Data with the XML
Document Stores
This section provides a brief summary of
the methods and properties that you can use to load and save XML data from
document stores. It will help you to choose the correct one, and help you see
how the techniques described in the remainder of this article relate to the
three objects. Table 1 lists the relevant methods and properties, showing which
are available on each of the three document stores, and the editing capability
of the XPathNavigator that is returned from the CreateNavigator method.
Table 1 - The
Methods and Properties for Reading and Writing XML with an XML Document Store
Method/Property | XmlDocument | XmlDataDocument | XPathDocument | Description |
Load | Yes | Yes | No | Loads XML from a Stream, a URI or disk file, an XmlReader or a TextReader. |
LoadXml | Yes | Yes | No | Loads the XML contained in a String. |
ReadNode | Yes | Yes | No | Creates an XmlNode instance from
the contents of an XmlReader,
which can then be inserted into the document. |
Save | Yes | Yes | No | Saves the XML content to a Stream, a URI or disk file, an XmlReader or a TextReader. |
WriteTo | Yes | Yes | No | Writes the complete current node and all its
content to an XmlWriter. |
WriteContentTo | Yes | Yes | No | Writes just the content of the current
node and its child nodes to an XmlWriter. |
InnerXml | Yes | Yes | No | Gets or sets the XML content of the
current node, including the markup it contains. |
InnerText | Yes | Yes | No | Gets or sets just the text content of the
current node and its child nodes. |
OuterXml | Yes | Yes | No | Gets or sets the XML content and
containing tags of the current node, including the markup it contains. |
CreateNavigator | Read/Write | Read/Write | Read Only | Creates an XPathNavigator at the
current location within the document. |
Table 2 shows the methods and properties of
the XPathNavigator class that relate to reading and writing XML content from an XML
document store to which it is attached. Notice that the CanEdit property can be queried to see if the underlying document store
supports editing of the XML content.
Table 2 - The
Methods and Properties for Reading and Writing XML with an XPathNavigator
Method/Property | Description |
ReadSubtree | Returns an XmlReader pointing to
the current node, allowing this node and its content to be read from the XML
document one node at a time, or streamed into another object such as XslCompiledTransform. |
WriteSubtree | Returns an XmlWriter pointing to
the current node, allowing this node and its content to be written to a disk
file, a Stream, or another writer object instance. |
InnerXml | Gets or sets the XML content of the
current node, including the markup it contains. Cannot be used to set the
content in an XPathDocument. |
OuterXml | Gets or sets the XML content and
containing tags of the current node, including the markup it contains. Cannot
be used to set the content in an XPathDocument. |
CanEdit | Indicates if this XPathNavigator supports editing of the XML document. |
Reading and Writing XML with an XPathDocument
As you can see from the tables above, the XPathDocument provides no read/write support directly. You create an XPathDocument by specifying the source XML in the constructor (there is no Load method), but you can only access the content in read-only fashion via
an XPathNavigator. We've created an example named xpathdocument.aspx that demonstrates the features for reading and writing XML with an XPathDocument instance, which we'll describe next. You can run or download all of
the samples from our Website at http://www.daveandal.net/articles/readwritexml/.
All the examples contain a [view source] link that you can use to view the source code.
Loading an XML Document
The first step is to load an XML document. Unlike
the other two XML document stores, the XPathDocument does not
have a Load method - so you must specify a Stream, an XmlReader, a TextReader or the URI or path of a file or resource that contains the XML in
the constructor. You can optionally specify the white-space handling when using
an XmlReader, or a URI/file path. The available values are XmlSpace.Default, XmlSpace.None and XmlSpace.Preserve.
The code below, taken from our example,
creates a StringBuilder to hold the results for the page, and the paths to the input and
output documents we'll be using:
Dim builder As New StringBuilder()
Dim xp As XPathDocument = Nothing
Dim sInPath As String = Server.MapPath("data/slides.xml")
Dim sOutPath As String = Server.MapPath("output/writesubtree.xml")
Try
' create an XPathDocument containing the
XML document
xp = New XPathDocument(sInPath)
builder.Append("<p><b>Loaded
XPathDocument</b> with " & sInPath & "</p>")
Catch ex As Exception
builder.Append("<p><b>ERROR
creating XPathDocument:</b><br />")
builder.Append("Message = "
& ex.Message & "</p>")
Return
End Try
Creating an XPathNavigator and Displaying its Properties
To access the content, we must create an XPathNavigator over the document. In the next section of code, we do this and move
it to the first slide element in the document. Then we can display the values of the
properties you saw listed in Table 1:
' create XPathNavigator over document and
move to first <slide> element
Dim xn As XPathNavigator = xp.CreateNavigator()
xn.MoveToFirstChild() ' move to root
element
xn.MoveToFirstChild() ' move to session
element
xn.MoveToFirstChild() ' move to slides
element
xn.MoveToFirstChild() ' move to slide
element
builder.Append("<p><b>Created
XPathNavigator</b> and moved to the first <b>" _
& xn.Name & "</b>
element</p>")
' display property values from
XPathNavigator
builder.Append("<p>XPathNavigator.<b>CanEdit</b>
property = " _
& xn.CanEdit.ToString() &
"</p>")
builder.Append("<p>XPathNavigator.<b>OuterXml</b>
property = " _
&
Server.HtmlEncode(xn.OuterXml) & "</p>")
builder.Append("<p>XPathNavigator.<b>InnerXml</b>
property = " _
& Server.HtmlEncode(xn.InnerXml)
& "</p>")
Figure 1 shows the results of running this
page, and you can see the values of the properties extracted in the previous
section of code. Notice that the CanEdit property returns False, because the XPathDocument is read-only and so the XPathNavigator reflects this. You can also see the difference between the InnerXml and OuterXml properties in this screenshot - the InnerXml property does
not include the start and end tags of the slide element.

Figure 1 - Reading and writing XML with an XPathDocument instance
Using the ReadSubtree Method of the XPathNavigator
Next the code calls the ReadSubtree method of the XPathNavigator to get back an XmlReader positioned at the current node in the document (the first slide node). Using code similar to that we described in the first article
in this series, we can iterate through the nodes exposed by the XmlReader and display the contents. If you look back at Figure 1 you'll see
the results:
' create XmlReader and read current node
using ReadSubtree method
Try
Dim xr As XmlReader = xn.ReadSubtree()
builder.Append("<p>Values from
the <b>ReadSubtree</b> method:<br />")
While xr.Read()
If xr.IsStartElement() Then
' write out element name
builder.Append("Element Name:
" & xr.Name & "<br />")
' see if this element has any
attributes
If xr.HasAttributes Then
While xr.MoveToNextAttribute()
' iterate through the attributes
displaying the
' name and the value of each one
builder.Append(" - Attribute
Name: " & xr.Name)
builder.Append("
Value: '" & xr.Value & "'<br />")
End While
End If
End If
' if this is a text node then just
display the value.
If xr.NodeType = XmlNodeType.Text Then
builder.Append("Element Value:
'" & xr.Value & "'" & "<br />")
End If
End While
builder.Append("</p>")
Catch ex As Exception
builder.Append("<p><b>ERROR
executing the ReadSubtree method:</b><br />")
builder.Append("Message = "
& ex.Message & "</p>")
End Try
Using the WriteSubtree Method of the XPathNavigator
While the ReadSubtree method
returns an XmlReader, the WriteSubtree method accepts instead an existing XmlWriter instance, and
writes the content of the current node to that writer. The following code shows
the XmlWriter being created with an
XmlWriterSettings instance, and static Create method (as
described in the previous article). The writer is then passed to the WriteSubtree method. Afterwards, the disk file is read using the File.ReadAllText method and added to the StringBuilder, the contents of which are
then dumped into a Label control on the page to display the results:
' create XmlWriter and write contents of
current element to disk
Dim ws As New XmlWriterSettings()
ws.Indent = True
Dim xw As XmlWriter = Nothing
Try
xw = XmlWriter.Create(sOutPath, ws)
xn.WriteSubtree(xw)
Catch ex As Exception
builder.Append("<p><b>ERROR
executing the WriteSubtree method:</b><br />")
builder.Append("Message = "
& ex.Message & "</p>")
Finally
xw.Close()
End Try
' read new disk file and display content in
the page
builder.Append("<p>Contents of
file created with the <b>WriteSubtree</b> method:")
builder.Append("<pre>"
& Server.HtmlEncode(File.ReadAllText(sOutPath)) &
"</pre></p>")
' display the results
Label1.Text = builder.ToString()
Looking back at Figure 1, you can see that
the WriteSubtree method creates the following XML document to represent the contents
of the first slide node:
<?xml version="1.0" encoding="utf-8"?>
<slide position="1" xmlns="http://myns/slidesdemo">
<title>Agenda</title>
<rv:reviewed
xmlns:rv="http://myns/slidesdemo/reviewdate">2004-05-10T00:00:00</rv:reviewed>
</slide>