Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp .NET Programming (2002) [eng]

.pdf
Скачиваний:
58
Добавлен:
16.08.2013
Размер:
7.98 Mб
Скачать

...

FileStream fs;

private void btnDeserial_Click(object sender, System.EventArgs e) { try{

XmlSerializer theSerial = new XmlSerializer (typeof(OurTeam)); fs = new FileStream("employee.xml",FileMode.Open);

OurTeam newteam = new OurTeam();

newteam = (OurTeam) theSerial.Deserialize(fs); foreach (Employee emp in newteam){

lstFname.Items.Add(emp.Fname);

lstLname.Items.Add(emp.Lname);

lstTitle.Items.Add(emp.Title);

}

}

catch (Exception excep){ MessageBox.Show (excep.Message);

}

finally{

fs.Close();

}

}

Finally, the Employee items in the collection class can be cycled through, and their XML elements accessed like instance members. The results of adding the XML elements, using instance member syntax, to ListBoxes, is shown in Figure 12.13.

Figure 12.13: Once the XML has been deserialized from the file back to class instances, it can be accessed normally.

Reading and Writing XML

You don’t need to serialize XML content to write (and read) XML documents to and from files. The XmlTextWriter class allows you to write whatever XML you care to generate, and XmlTextReader lets you read the contents of an XML document. Using the XMLTextWriter, an XML document is written from top to bottom. In other words, you get a single forward pass at creating the XML document.

To see this in action, I’ve set up a simple interface, shown in Figure 12.14, which lets the user decide the name for XML nodes and the values that those nodes enclose. The text entered by the user in the Element text box will become the XML tag, and the text entered in the Value text box will become its value:

<element> value </element>

Figure 12.14: Users can enter as many element/value pairs as they want; when all the pairs have been entered, the XML document is created.

In the interests of simplicity, all the element/value pairs entered by the user will fall under one root XML node, <UserChoice>. So the structure of the XML document will look like this, with each element/value combination chosen by the user:

<UserChoice> <element> value </element> <element>

value </element>

...

</UserChoice>

Creating the Class and Collection Class

If the user can enter multiple element/value pairs before the XML document is generated, the application must have some way to store the information. Once again, we’ll use a class that is based on ArrayList to handle this (this time inheriting directly from ArrayList rather than using ArrayList functionality via encapsulation as in the example in the previous section).

Listing 12.9 shows the bare-bones class that will be used for each element/value pair and the collection class that will be used to store the user’s XML choices until the XML document is ready to be generated.

Listing 12.9: An Element/Value XML Class and a Collection Class Used to Store Element/Value Pairs

using System.Collections;

...

public class Xml

{

public string

element;

public string

@value;

}

public class XmlCollection : ArrayList { public void Add (Xml aXml){

base.Add (aXml);

}

}

Note I really wanted to use the identifier value for the variable holding the value of an

element, but, of course, value is a reserved keyword in C#. So I marked the variable name with the @ literal identifier, @value, which can be used as a variable.

Using the Class and Collection

After you’ve set up the class and collection, create an interface with text boxes for the user input of elements and values, like that shown in Figure 12.14. The Add button will be used to add an element/value pair to the collection, and the Write button will be used to generate the relevant XML document.

At the top of the form module, import the System.Xml namespace (it contains the XmlTextReader and XmlTextWriter classes):

using System.Xml;

Next, declare at the form level and instantiate in Form1’s constructor a collection object based on the collection class defined in Listing 12.9:

XmlCollection theXml; // Declared at form level

...

theXml = new XmlCollection(); // in Form1 constructor

In the click event procedure of the Add button, instantiate an element/value object, assign the values of the text boxes to it, and add it to the collection:

private void btnAdd_Click(object sender, System.EventArgs e) { Xml aXml = new Xml();

aXml.element = txtElement.Text; aXml.@value = txtValue.Text; theXml.Add (aXml); txtElement.Text = String.Empty; txtValue.Text = String.Empty;

}

That takes care of adding element/value pairs to the collection. Now let’s write an XML document based on the contents of the collection!

In the click event procedure for the Write button, declare and instantiate a XmlTextWriter, specifying a file (including path) and the encoding to use:

XmlTextWriter myXmlTextWriter = new XmlTextWriter("doc.xml", null);

Setting the encoding to null causes it to be written out as UTF-8. If no path is provided, by default the file will be written to the executable, or bin, directory of the project.

Warning The XmlTextWriter will overwrite the specified file if it already exists. There’s nothing to stop you from using the common dialogs (or other mechanism) to allow the user to select a file.

Next, declare an element/value XML object based on the class defined in Listing 12.9.

Use the methods and properties of the XmlTextWriter object to tell the XmlTextWriter to format the XML with three spaces for indentation, place a comment at the start of the document, and create a root XML element called <UserChoice>:

myXmlTextWriter.Formatting = Formatting.Indented; myXmlTextWriter.Indentation = 3; myXmlTextWriter.IndentChar = ' '; myXmlTextWriter.WriteStartDocument();

myXmlTextWriter.WriteComment("This is a sample XML document" + " generated using an XmlTextWriter object.");

myXmlTextWriter.WriteStartElement("UserChoice");

Next, use the foreach syntax to cycle through the collection to write each item in the collection:

foreach (Xml aXml in theXml){ myXmlTextWriter.WriteElementString(aXml.element, aXml.@value);

}

Finally, close the root element and document:

myXmlTextWriter.WriteEndElement();

myXmlTextWriter.Close();

Note I just wrote “finally” close the root element and document. Well, as you may be thinking, the closure code in this example (and in some others in this chapter) should be placed in the finally block of a try...finally structure. I haven’t done this here to make the code more readable—but you should place this kind of closure code within finally blocks in all real-world applications.

The complete code for adding XML element/value pairs to the collection and writing an XML document based on the collection items that have been added is shown in Listing 12.10.

Listing 12.10: Creating a Collection of Element/Value Pairs and Writing Them to an XML Document

using System.Xml;

...

XmlCollection theXml; // Declared at form level

...

theXml = new XmlCollection(); // in Form1 constructor

...

private void btnAdd_Click(object sender, System.EventArgs e) { Xml aXml = new Xml();

aXml.element = txtElement.Text; aXml.@value = txtValue.Text; theXml.Add (aXml); txtElement.Text = String.Empty; txtValue.Text = String.Empty;

}

private void btnWrite_Click(object sender, System.EventArgs e) { XmlTextWriter myXmlTextWriter = new XmlTextWriter("doc.xml", null); myXmlTextWriter.Formatting = Formatting.Indented; myXmlTextWriter.Indentation = 3;

myXmlTextWriter.IndentChar = ' '; myXmlTextWriter.WriteStartDocument(); myXmlTextWriter.WriteComment("This is a sample XML document" +

" generated using an XMLTextWriter object."); myXmlTextWriter.WriteStartElement("UserChoice"); foreach (Xml aXml in theXml){

myXmlTextWriter.WriteElementString(aXml.element, aXml.@value);

}

myXmlTextWriter.WriteEndElement();

myXmlTextWriter.Close();

}

Run the project and enter some element/value pairs, clicking Add each time. Next, click the Write button. You’ll find that an XML document, containing the elements and values you added, has been created along these lines:

<?xml version="1.0"?>

<!--This is a sample XML document generated using an XmlTextWriter object.- ->

<UserChoice>

<Protagonist>Nicholas Nickleby</Protagonist>

<Schoolmaster>Wackford Squeers</Schoolmaster> <InfantPhenomenon>Ninetta Crummles</InfantPhenomenon>

</UserChoice>

Note You could certainly extend this application if you were interested or needed to. For example, this simple application doesn’t check to make sure that each element added is unique. Duplicate checking could be implemented by calling the Exists method of the XmlCollection class before adding an element to the collection.

Reading XML with XmlTextReader

The counterpart to the XmlTextWriter class is the XmlTextReader class. Realize that this is by no means the only class available in .NET to read (or parse) XML files; XmlNodeReader and XmlValidatingReader (used in the example in the previous section of this chapter) are also powerful classes.

Much of the time, you will know a great deal about the formatting of the XML file you want to read. That’s certainly the case with the example that I’ll show you, in which we know that the root element is named <UserChoice> and is followed by elements and values:

<UserChoice> <element> value </element> <element>

value </element>

...

</UserChoice>

If you are interested in only the elements and values, it’s an easy thing to instantiate a new XmlTextReader object and start it past the root element:

XmlTextReader myXmlTextReader = new XmlTextReader("doc.xml"); myXmlTextReader.ReadStartElement("UserChoice");

You could then set up a loop reading the rest of the document, and exiting only when the end of the document or the </UserChoice> tag is reached:

while (myXmlTextReader.Read()){

if (myXmlTextReader.Name == "UserChoice") break;

...

}

Within the loop, you can use the ReadOuterXml method to read tags and values into a multiline text box:

while (myXmlTextReader.Read()){

if (myXmlTextReader.Name == "UserChoice") break;

txtReader.Text += myXmlTextReader.ReadOuterXml() + "\r\n";

}

Finally, the XmlTextReader should be closed:

myXMLTextReader.Close()

The code, placed in the Display button’s click event, is shown in Listing 12.11. Listing 12.11: Reading an XML File

private void btnDisplay_Click(object sender, System.EventArgs e) { txtReader.Text = String.Empty;

XmlTextReader myXmlTextReader = new XmlTextReader("doc.xml"); myXmlTextReader.ReadStartElement("UserChoice");

while (myXmlTextReader.Read()){

if (myXmlTextReader.Name == "UserChoice") break;

txtReader.Text += myXmlTextReader.ReadOuterXml() + "\r\n";

}

myXmlTextReader.Close();

}

If you run the project and click the Display button, the elements and values in the doc.xml file contained between the beginning <UserChoice> tag and the ending </UserChoice> tag will be shown in the text box (Figure 12.15).

I leave it, as they say, as an exercise for the reader to refine this project by adding exception handling and dealing with white space entered by the user for an element tag (as things stand, this will cause the program to throw an exception when XmlTextReader tries to retrieve the tag with white space)—and to customize the application for your particular needs.

Figure 12.15: The XmlTextReader is programmed to retrieve the tags and values between the <UserChoice> and </UserChoice> tags.

The Document Object Model (DOM)

The XmlTextWriter and XmlTextReader classes described in the last section are great if all you need to do is make a single pass through an XML document—and read or write it to a file. However, they don’t help you that much with adding, editing, and deleting members of an XML document. Other mechanisms exist that do a better job of this, including SAX (see sidebar) and the Document Object Model (DOM)—implemented by the XmlDocument class and related classes—which is discussed in this section.

Simple API for XML (SAX)

Simple API for XML (SAX) is an alternative to DOM that is not supported explicitly by any

.NET classes. (As a publicly developed interface, SAX could fairly easily be implemented in a class derived from XmlReader.)

The SAX interface reads an XML document using an events-based model. A SAX reader is fast, read-only, and only moves in one direction: forward.

SAX requires less overhead than DOM, so it might make sense to use it if resources are an issue and you have a large number of XML documents to parse (or a single, very large document). SAX can be configured to read only the information you need (unlike DOM, which reads the entire XML document into memory). In additionunlike DOMa SAX parse can be stopped when you find what you need.

Once an XML document has been loaded into an instance of the XmlDocument class, it is available in memory and can be traversed backwards and forwards. The parts of the XML document are represented by node classes, some of which are shown in Table 12.3. These

classes represent elements, attributes, and so on. You can add, edit, or delete node classes belonging to an active XmlDocument instance.

 

 

Table 12.3: Types of XmlDocument Nodes

 

 

 

 

 

DOM Node

 

.NET Class

 

Description

 

 

 

 

 

Document

 

XmlDocument

 

The container of all the nodes in

 

 

 

 

thetree. It is also known as the

 

 

 

 

document root (not always the same

 

 

 

 

as the root element).

 

 

 

 

 

DocumentFragment

 

XmlDocumentFragment

 

Temporary XML storage containing

 

 

 

 

one or more nodes without any tree

 

 

 

 

structure.

 

 

 

 

 

DocumentType

 

XmlDocumentType

 

Represents the <!DOCTYPE…>

 

 

 

 

node.

 

 

 

 

 

Element

 

XmlElement

 

Represents an element node.

 

 

 

 

 

Attr

 

XmlAttribute

 

An attribute of an element.

 

 

 

ProcessingInstruction

 

XmlProcessingInstruction

 

A processing instruction node.

 

 

 

 

 

Comment

 

XmlComment

 

A comment node.

 

 

 

 

 

Text

 

XmlText

 

Text belonging to an element or

 

 

 

 

attribute.

 

 

 

 

 

Note For a complete list of XML nodes, look up “Types of XML Nodes” in online help. The topic describes nodes (not shown in Table 12.3) that can be used in .NET but are not approved by W3C.

Figure 12.16 shows how the DOM classes interact. As you can see in the figure, nodes in an XmlDocument can be accessed and managed using the XmlNode class. They can also be accessed and managed using a class that depends on the specific type of the node, e.g., XmlAttribute and XmlElement.

Figure 12.16: Interaction of the XML Document Object Model (DOM) classes.

Note You may also be interested in the XmlDataDocument class, which is derived from

XmlDocument. XmlDataDocument allows you to load relational data and manipulate it as XML using the DOM.

As you can see, the classes and mechanisms related to DOM and the XmlDocument class are complex and powerful. It would take at least a chapter to cover them all—actually, more like a book, a big fat book.

Obviously, I don’t have space to explain all the DOM-related .NET classes and how they work. But I can provide a demonstration that allows you to load an XmlDocument object from a file, and then move recursively through the nodes in the XmlDocument, displaying representations of them in a TreeView control. (The complete code will be shown in Listing 12.12.)

To start with, I added a Button, an OpenFileDialog, and a TreeView control to a form. At the form level, I declared some variables to hold the TreeView nodes, the XMLDocument nodes, and an XmlTextReader:

TreeNode tn;

XmlNode xn;

XmlTextReader xr;

Next, within the button’s click event, I added the standard common-dialog code to allow the user to select an XML file. Note that not all XML files end with a .xml suffix; in Figure 12.17, the user is shown selecting a Web.config XML file. However, if a non-XML file is selected (whatever the suffix), the program will throw an exception.

Figure 12.17: The user can select an XML file for parsing.

With a file selected, an XmlTextReader and an XmlDocument are instantiated:

xr = new XmlTextReader(theFile);

...

XmlDocument xDoc = new XmlDocument()

The Load method of the XmlDocument is used to connect the selected file to the XmlDocument:

xDoc.Load (xr);

The top node of the TreeView is created and the WalkTheTree method invoked:

tn = new TreeNode("XML Document"); tView.Nodes.Add(tn);

xn = xDoc.DocumentElement; WalkTheTree (xn, tn);

Within WalkTheTree, a node is added to the topmost node, using the XML name and value of the node for display purposes:

XmlNode tmpXn; TreeNode tmpTn; TreeNode tmpTnAttr;

tmpTn = new TreeNode (xn.Name + " " + xn.Value); tn.Nodes.Add(tmpTn);

Attributes need special handling:

if (xn.NodeType == XmlNodeType.Element){ foreach (XmlNode xnAttr in xn.Attributes){

tmpTnAttr = new TreeNode(xnAttr.Name + " = " + xnAttr.Value); tmpTn.Nodes.Add(tmpTnAttr);

}

}

If the node has children, then process them, and their children and siblings, by recursively calling WalkTheTree:

if (xn.HasChildNodes){ tmpXn = xn.FirstChild; while (tmpXn != null){

WalkTheTree(tmpXn, tmpTn); tmpXn = tmpXn.NextSibling;

}

}

If you try this on an XML file, such as the Web.config file selected earlier, you’ll have to expand the nodes in the TreeView. With the nodes expanded, the element name, value, and attributes will be displayed (Figure 12.18).