Creating XML Documents

If your primary goal is analyzing the contents of an XML document, you will probably find the XML DOM parsing model much more effective than readers in spite of the larger memory footprint and set-up time it requires. A document loaded through XML DOM can be modified, extended, shrunk, and, more important, searched. The same can’t be done with XML readers; XML readers follow a different design center. But what are the advantages of creating XML documents using XML DOM?

To create an XML document using the XML DOM API, you must first create the document in memory and then call the Save method or one of its overloads. This system gives you great flexibility because no changes you make are set in stone until you save the document. In general, however, using the XML DOM API to create a new XML document is often overkill unless the creation of the document is driven by a complex and sophisticated logic.

In terms of the internal implementation, it is worth noting that the XML DOM’s Save method makes use of an XML text writer to create the document. So unless the content to be generated is complex and subject to a lot of conditions, using an XML text writer to create XML documents is faster.

The XmlDocument class provides a bunch of methods to create new nodes. These methods are named consistently with the writing methods of the XmlTextWriter class we encountered in Chapter 4. You’ll find a CreateXXX method for each WriteXXX method provided by the writer. Actually, each CreateXXX method simply creates a new node in memory, and the corresponding WriteXXX method on the writer simply writes the node to the output stream.

Appending Nodes

Let’s look at how to create a brand-new XML document persisting to XML the subdirectories found below a given path. The basic algorithm to implement can be summarized in the following steps:

  1. Create any necessary nodes.

  2. Link the nodes to create a tree.

  3. Append the tree to the in-memory XML document.

  4. Save the document.

The expected final output has the following layout:

<folders ...>
    <folder ...>text</folder>
    <folder ...>text</folder>
    ...
</folders>

The following code creates the XML prolog and appends to the Xml­Document instance the standard XML declaration and a comment node:

XmlDocument doc = new XmlDocument();
XmlNode n;

// Write and append the XML heading
n = doc.CreateXmlDeclaration("1.0", "", "");
doc.AppendChild(n);

// Write and append some comment
n = doc.CreateComment(" Content of the "" + path + "" folder ");
doc.AppendChild(n);

The CreateXmlDeclaration method takes three arguments: the XML version, the required encoding, and a Boolean value denoting whether the document can be considered stand-alone or has dependencies on other documents. All arguments are strings, including the encoding argument, as shown here:

<?xml version="1.0" standalone="yes" encoding="utf-7"?>

If specified, the encoding is written in the XML declaration and used by Save to create the actual output stream. If the encoding is null or empty, no encoding attribute is set, and the default Unicode Universal Character Set Transformation Format, 8-bit form (UTF-8) encoding is used.

CreateXmlDeclaration returns an XmlDeclaration node that you add as a child to the XmlDocument class. CreateComment, on the other hand, creates an XmlComment node that represents an XML comment, as shown here:

<!-- Content of the c: folder -->

Element nodes are created using the CreateElement method. The node is first configured with all of its expected child nodes and then added to the document, as shown here:

XmlNode root = doc.CreateElement("folders");

For the purposes of this example, we need a way to access all the subdirectories of a given folder. In the .NET Framework, this kind of functionality is provided by the DirectoryInfo class in the System.IO namespace:

DirectoryInfo dir = new DirectoryInfo(path);

To scan the subdirectories of the given path, you arrange a loop on top of the array of DirectoryInfo objects returned by the GetDirectories method, as follows:

foreach (DirectoryInfo d in dir.GetDirectories())
{
    n = doc.CreateElement("folder"); 

    //
    // Create attributes for the <folder> node
    // 

    // Set the text for the node
    n.InnerText = "Content of " + d.Name;

    // Append the node to the rest of the document
    root.AppendChild(n);
}

In the loop, you create any needed <folder> node, configure the node with attributes and text, and then append the node to the parent <folders> node.

When creating an element node using the CreateElement method, you can specify a namespace URI as well as a namespace prefix. With the following code, you add an xmlns attribute to the node declaration:

XmlNode root = doc.CreateElement("folders", "urn:dino-e");

The final result is shown here:

<folders xmlns="urn:dino-e">

If you use a namespace, you might reasonably want to use a prefix too. To specify a namespace prefix, resort to another overload for the CreateElement method in which you pass in the order, the prefix, the local name of the element, and the namespace URI, as shown here:

XmlNode root = doc.CreateElement("d", "folders", "urn:dino-e");

The node XML code changes to this:

<folders xmlns:d="urn:dino-e">

At this point, to also qualify the successive <folder> nodes with this namespace, call CreateElement with the prefix and the URI, as shown here:

n = doc.CreateElement("d", "folder", "urn:dino-e");

Note

Bear in mind that although all the CreateXXX methods available in the XmlDocument class can create an XML node, that node is not automatically added to the XML DOM. You must do that explicitly using one of the several methods defined to extend the current DOM.


Appending Attributes

An attribute is simply a special type of node that you create using the CreateAttribute method. The method returns an XmlAttribute object. The following code shows how to create a new attribute named path and how to associate it with a parent node:

XmlAttribute a;
a = doc.CreateAttribute("path");
a.Value = path;
node.Attributes.SetNamedItem(a);

Like CreateElement, CreateAttribute too allows you to qualify the name of the attribute using a namespace URI and optionally a prefix. The overloads for both methods have the same signature.

You set the value of an attribute using the Value property. At this point, however, the attribute node is not yet bound to an element node. To associate the attribute with a node, you must add the attribute to the node’s Attributes collection. The SetNamedItem method does this for you. The following code shows the finalized version of the loop that creates the XML file for our example:

foreach (DirectoryInfo d in dir.GetDirectories())
{
    n = doc.CreateElement("folder"); 
    a = doc.CreateAttribute("name");
    a.Value = d.Name;
    n.Attributes.SetNamedItem(a);

    a = doc.CreateAttribute("created");
    a.Value = d.CreationTime.ToString();
    n.Attributes.SetNamedItem(a);
    root.AppendChild(n);
    n.InnerText = "Content of " + d.Name;
}

Figure 5-5 demonstrates the structure of the newly created XML file.

Figure 5-5. An XML file representing a directory listing created using the XML DOM API.


Persisting Changes

The final step in saving the XML document we have created is to attach the <folders> node to the rest of the document and save the document, as shown here:

doc.AppendChild(root);
doc.Save(fileName);

To persist all the changes to a storage medium, you call the Save method, which contains four overloads, shown here:

public virtual void Save(Stream);
public virtual void Save(string);
public virtual void Save(TextWriter);
public virtual void Save(XmlWriter);

The XML document can be saved to a disk file as well as to an output stream, including network and compressed streams. You can also integrate the class that manages the document with other .NET Framework applications by using writers, and you can combine more XML documents using, in particular, XML writers.

Whatever overload you choose, it is always an XML writer that does the job of persisting XML nodes to a storage medium. The XmlDocument class makes use of a specialized version of the XmlTextWriter class that simply works around one of the limitations of XML writers.

XML writers do not allow you to write element and attribute nodes for which you have a prefix but an empty namespace. If the namespace URI is set to null, the writer successfully looks up the closest definition for that prefix and figures out the namespace, if one exists. If the namespace is simply an empty string, however, an ArgumentException exception is thrown. The XML DOM internal writer overrides the WriteStartElement and WriteStartAttribute methods. If the namespace URI is empty when the prefix is not, the new overrides reset the prefix to the empty string and no exception is raised.

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

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