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

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1

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

558 C H A P T E R 1 2 A D O . N E T A N D D A T A B A S E D E V E L O P M E N T

Done! Figure 12-21 shows the final results of the second example.

Figure 12-21. Maintaining authors using a DataGridView

Summary

In this chapter, you saw a large portion of the .NET Framework’s ADO.NET. You started out by covering the basics of ADO.NET. You then moved on to creating a database to work with through the rest of the chapter using Visual Studio 2005. Next, you explored how to connect, query, insert, update, delete, count, and sum rows of a database using connected access to the database. Finally, you learned how to do the same things with disconnected access, in the process building a couple simple Windows Forms author maintenance tools.

You have now learned the code you will need to implement ADO.NET in either a connected or disconnected manner. The world of databases should now be open to you when you create your applications.

In the next chapter, you’ll examine the mysterious world of XML, the last of the four common methods of getting input into and out of your .NET Windows applications.

C H A P T E R 1 3

■ ■ ■

XML

Though you’re covering XML last of the four most common .NET Framework class library input/output (I/O) mechanisms, it’s hardly the least important. In fact, much of the underlying architecture of

.NET relies on XML, so much so that the .NET Framework class library provides a plethora of ways of working with XML. This chapter covers some of the more common classes.

A major goal of the .NET Framework class library is to simplify XML development. It has done this. But if you come from a background of implementing XML in the worlds of Microsoft XML Parser (MSXML) or Java, what you’ve already learned isn’t lost. In fact, you’ll find many similarities between these implementations and the one provided by the .NET Framework class library.

This chapter isn’t intended to provide details about XML, though to provide a level playing field I do include some high-level coverage. Instead, the goal is to show you how to implement the many facets of XML development provided by the .NET Framework class library. In particular, you’ll learn how to read, write, update, and navigate an XML file. After you’ve covered the common areas of XML that you’ll more than likely develop code for, you’ll move on and look at using XML with ADO.NET.

What Is XML?

First off, XML is not a computer language. Rather, it is a meta-language for defining or specifying how to mark up a document in such a way as to identify its structure.

Say what?

How about this definition: XML is a method of arranging a document so that it’s broken up into parts. For example, in this chapter you’re going to create an XML document of role-playing monsters. The document will be broken up by monster name, hit dice, and weapon(s). (If you play Dungeons & Dragons [D&D], you know this is a very small subset of all the information available, but I didn’t want or need to make the examples any more difficult.)

XML documents, in their simplest form, are made up of a hierarchy of two types of components: elements and attributes.

An element is made up of three parts:

Start element node, often called the start tag. It is made up of an element text name enclosed in angle brackets: <Element_Tag>.

Content node(s) made up of a combination of zero or more text nodes (text enclosed between start and end element nodes) and child or nested elements (hence the hierarchical nature of XML).

End element node, often called the end tag. It is made up of a backslash and text, which must exactly match the text name of the start element node, enclosed in angle brackets:

</Element_Tag>.

559

560 C H A P T E R 1 3 X M L

An attribute is an extension to the start element node. It provides more information about the element. Attributes are one or more name= "value" pairs added after the element text name but before the closing angle bracket: <Element_Tag name="value" >.

Two additional components that you will encounter are the XML header declaration and the comment. The header declaration indicates that the file should be parsed as XML and in most cases will simply read

<?xml version="1.0" encoding="utf-8"?>

Comments provide the reader of the XML file additional information that will be ignored by the XML parser. The syntax of a comment is <!-- comment_text -->.

Listing 13-1 shows the XML document that you’ll be using throughout the chapter.

Listing 13-1. An XML Monster File

<?xml version="1.0" encoding="utf-8"?> <!-- Monster List -->

<MonsterList>

<!-- Easy Monster --> <Monster>

<Name>Goblin</Name>

<HitDice Dice="1d8" Default="4"/>

<Weapon Number="1" Damage="1d4">Dagger</Weapon> </Monster>

<!-- Medium Monster --> <Monster>

<Name>Succubus</Name>

<HitDice Dice="6d8+6" Default="33"/>

<Weapon Number="2" Damage="1d3+1">Claw</Weapon> <Weapon Number="1" Damage="1d4">Dagger</Weapon>

</Monster>

<!-- Tough Monster --> <Monster>

<Name>Red Dragon</Name>

<HitDice Dice="22d12+110" Default="253"/> <Weapon Number="1" Damage="2d8">Bite</Weapon> <Weapon Number="2" Damage="2d6">Claw</Weapon> <Weapon Number="2" Damage="1d8">Wing</Weapon>

</Monster>

</MonsterList>

The .NET Framework XML Implementations

The .NET Framework class library provides two ways of processing XML data:

Fast, noncached, forward-only stream

Random access via an in-memory Document Object Model (DOM) tree

Both methods of processing XML data are equally valid. However, each has a definite time when it is better suited. At other times, both will work equally well, and the decision of which to use is up to the developer’s taste.

C H A P T E R 1 3 X M L

561

The major deciding factors for choosing one method over the other are whether all data needs to be in memory at one time (large files take up large amounts of memory, which in many cases isn’t a good thing, but with the large amount of physical memory a computer can have nowadays the size of the XML file is almost irrelevant) and whether random access to the data is needed. When either of these factors occurs, the DOM tree should probably be used because the process of repeatedly starting from the beginning of the document and reading forward sequentially through it to find the right place in the stream of XML to read, update, or write random data is time consuming.

On the other hand, if the data can be processed sequentially, a forward-only stream is probably the better choice because it is easier to develop and uses fewer resources more efficiently than a DOM tree. However, there is nothing stopping you from using a DOM tree in this scenario as well.

Implementing code to process XML with the .NET Framework class library requires referencing the System.Xml.dll assembly. You would think that due to the heavy reliance on XML in the .NET Framework, it would be part of the mscorlib.dll assembly. Because it is not, your source code implementing XML requires the following code be placed at the top of your source code (this is done automatically for you by Visual Studio 2005 for Windows Forms applications but not for console applications):

#using <system.xml.dll>

Six namespaces house all of the XML functionality within the .NET Framework class library. Table 13-1 describes these namespaces at a high level.

Table 13-1. XML Namespaces

Namespace

Description

System::Xml

Provides the core of all XML functionality

System::Xml::Schema

Provides support for XML Schema definition language

 

(XSD) schemas

System::Xml::Serialization

Provides support for serializing objects into XML

 

formatted documents or streams

System::Xml::XPath

Provides support for the XPath parser and

 

evaluation engine

System::Xml::Xsl

Provides support for Extensible Stylesheet Language

 

Transformations (XSLT) transforms

 

 

Forward-Only Access

Forward-only access to XML is amazingly fast. If you can live with the restriction that you can process the XML data only in a forward-only method, then this is the way to go. The base abstract classes for implementing this method of access are named, intuitively enough, XmlReader and

XmlWriter.

The .NET Framework class library’s implementation of forward-only access, when you first look at it, seems a lot like the Simple API for XML (SAX), but actually they are fundamentally different. Whereas SAX uses a more complex push model, the class library uses a simple pull model. This means that a developer requests or pulls data one record at a time instead of having to capture the data using event handlers.

562 C H A P T E R 1 3 X M L

Coding using the .NET Framework class library’s implementation of forward-only access seems, to me, more intuitive because you can handle the processing of an XML document as you would a simple file, using a good old-fashioned while loop. There is no need to learn about event handlers or SAX’s complex state machine.

Reading from an XML File

To implement forward-only reading of an XML file you use the XmlReader class. This is a little tricky as the XmlReader is an abstract class. This means, instead of the normal creation of the class using a constructor, you use the static method Create(), in conjunction with the optional

XmlReaderSettings class.

For those of you who developed XML code with a previous version of the .NET Framework, it is also possible to still use the XmlTextReader and XmlNodeReader classes, which inherit from XmlReader. With .NET Framework version 2.0, Microsoft has recommended the use of the XmlReader class, since using the Create() method with the XmlReaderSettings class you get the following benefits:

The ability to specify the features you want supported by the created XmlReader instance.

The ability to create an instance of XmlReaderSettings that can be reused to create multiple XmlReaders, each sharing the same features.

The ability to create a unique instance or modify an existing instance of the XmlReaderSettings, allowing each XmlReader to have a different set of features.

You can extend the features of the XmlReader. The Create() method can accept another XmlReader. The underlying XmlReader instance can be a reader such as an XmlTextReader, or another user-defined XmlReader instance that you add your own features to.

Provides the ability to take advantage of all the new features added to the XmlReader class in .NET Framework version 2.0. Some features, such as better conformance checking and compliance to the XML 1.0 recommendation, are only available with XmlReader instances created using the Create() method.

I’ll cover the XmlReaderSettings class when I discuss XML file validation later in the chapter, as much of this class pertains to validation.

The XmlReader class is made up of a number of properties and methods. Some of the more common properties you will probably encounter are as follows:

AttributeCount is an Int32 that specifies the number of attributes in the current Element, DocumentType, or XmlDeclaration node. Other node types don’t have attributes.

Depth is an Int32 that specifies the depth of the current node in the tree.

EOF is a Boolean that’s true if the reader is at the end of the file; otherwise, it’s false.

HasAttributes is a Boolean that’s true if the current node has attributes; otherwise, it’s false.

HasValue is a Boolean that’s true if the current node has a value; otherwise, it’s false.

IsEmptyElement is a Boolean that’s true if the current node is an empty element, or in other words, the element ends in />.

C H A P T E R 1 3 X M L

563

Item is the String value of an attribute specified by index or name within the current node.

LocalName is the String the local name of the current node. For example, Monster is the

LocalName for the element <my:Monster>.

Name is the String qualified name of the current node. For example, the fully qualified my:Monster is the Name for the element <my:Monster>.

NodeType is an XmlNodeType enum class that represents the node type (see Table 13-2) of the current node.

Prefix is the String namespace prefix of the current node. For example, my is the namespace for the element <my:Monster>.

ReadState is a ReadState enum class of the current state of the XmlReader object. Possible states are: Closed, EndOfFile, Error, Initial, and Interactive.

Value is the String value for the current node.

Here are a few of the more common XmlReader methods:

Close() changes the ReadState of the reader to Closed.

Create() is used to create an instance of an XmlReader.

GetAttribute() gets the String value of the attribute.

IsStartElement() returns the Boolean true if the current node is a start element tag.

MoveToAttribute() moves to a specified attribute.

MoveToContent() moves to the next node containing content.

MoveToElement() moves to the element containing the current attribute.

MoveToFirstAttribute() moves to the first attribute.

MoveToNextAttribute() moves to the next attribute.

Read() reads the next node.

ReadAttributeValue() reads an attribute containing entities.

ReadContentAs[data type]() reads the current content of the node as the [data type] specified. Examples are ReadContentAsInt() and ReadContentAsDouble().

ReadElementContentAs[data type]() reads the value of element node as the [data type] specified. Examples are ReadElementContentAsInt() and ReadElementContentAsDouble().

ReadElementString() is a helper method for reading simple text elements.

ReadEndElement() verifies that the current node is an end element tag and then reads the next node.

ReadStartElement() verifies that the current node is a start element tag and then reads the next node.

ReadString() reads the contents of an element or text node as a String.

Skip() skips the children of the current node.

564 C H A P T E R 1 3 X M L

The XmlReader class processes an XML document by tokenizing a text stream of XML data. Each token (or node, as it is known in XML) is then made available by the Read() method and can be handled as the application sees fit. A number of different nodes are available, as you can see in Table 13-2.

Table 13-2. Common XML Node Types

Node Type

Description

Attribute

An element attribute

Comment

A comment

Document

The root of a document tree providing access to the entire XML

 

document

DocumentFragment

A subtree of a document

DocumentType

A document type declaration

Element

A start element tag

EndElement

An end element tag

EndEntity

The end of an entity declaration

Entity

The start of an entity declaration

EntityReference

A reference to an entity

None

The value placed in NodeType before any Read() method is called

SignificantWhitespace

White space between markups in a mixed content model or white

 

space within the xml:space="preserve" scope

Text

The text content

Whitespace

White space between markups

XmlDeclaration

An XML declaration

 

 

The basic logic of implementing the XmlReader class is very similar to that of a file IO class:

1.Open the XML document.

2.Read the XML element.

3.Process the element.

4.Repeat steps 2 and 3 until the end of file (EOF) is reached.

5.Close the XML document.

The example in Listing 13-2 shows how to process the previous XML monster file. The output is to the console and contains a breakdown of the nodes that make up the XML file.

C H A P T E R 1 3 X M L

565

Listing 13-2. Splitting the XML Monster File into Nodes

#using <system.xml.dll>

using namespace System; using namespace System::Xml;

String ^indent(Int32 depth)

{

String ^ind = "";

return ind->PadLeft(depth * 3, ' ');

}

void main()

{

XmlReader ^reader;

try

{

reader = XmlReader::Create("Monsters.xml");

while (reader->Read())

{

switch (reader->NodeType)

{

case XmlNodeType::Comment: Console::WriteLine(

"{0}Comment node: Value='{1}'", indent(reader->Depth), reader->Value);

break;

case XmlNodeType::Element: Console::WriteLine(

"{0}Element node: Name='{1}'", indent(reader->Depth), reader->Name);

if (reader->HasAttributes)

{

while (reader->MoveToNextAttribute())

{

Console::WriteLine(

"{0}Attribute node: Name='{1}' Value='{2}'", indent(reader->Depth), reader->Name, reader->Value);

}

reader->MoveToElement();

}

566 C H A P T E R 1 3 X M L

if (reader->IsEmptyElement)

{

Console::WriteLine(

"{0}End Element node: Name='{1}'", indent(reader->Depth), reader->Name);

}

break;

case XmlNodeType::EndElement: Console::WriteLine(

"{0}End Element node: Name='{1}'", indent(reader->Depth), reader->Name);

break;

case XmlNodeType::Text: Console::WriteLine(

"{0}Text node: Value='{1}'", indent(reader->Depth), reader->Value);

break;

case XmlNodeType::XmlDeclaration: Console::WriteLine(

"Xml Declaration node: Name='{1}'", indent(reader->Depth), reader->Name);

if (reader->HasAttributes)

{

while (reader->MoveToNextAttribute())

{

Console::WriteLine(

"{0}Attribute node: Name='{1}' Value='{2}'", indent(reader->Depth), reader->Name, reader->Value);

}

}

reader->MoveToElement(); Console::WriteLine(

"End Xml Declaration node: Name='{1}'", indent(reader->Depth), reader->Name);

break;

case XmlNodeType::Whitespace: // Ignore white space break;

default:

Console::WriteLine(

"***UKNOWN*** node: Name='{1}' Value='{2}'", indent(reader->Depth), reader->Name, reader->Value);

}

}

}

C H A P T E R 1 3 X M L

567

catch (XmlException ^e)

{

Console::WriteLine("\n\n\nSplitting XML Aborted with error: {0}", e->Message);

}

finally

{

if (reader->ReadState != ReadState::Closed)

{

reader->Close();

}

}

}

The preceding code, though longwinded, is repetitively straightforward and, as pointed out, resembles the processing of a file in many ways.

One neat little trick this example shows is how you can use the XmlReader class’s Depth property to indent your output based on the depth the current node is within the tree. All I do is simply indent an additional three spaces for each depth:

String ^indent(Int32 depth)

{

String ^ind = "";

return ind->PadLeft(depth * 3, ' ');

}

You process all XML within an XmlException try/catch block because every XML method in the

.NET Framework class library can throw an XmlException.

You start by opening the XML file. Then you read the file, and finally you close the file. You place the Close() method in a finally clause to ensure that the file gets closed even on an exception. Before you close the file, you verify that the file had in fact been opened in the first place. It is possible for the Create() method of the XmlReader class to throw an XmlException and never open the XML file:

XmlReader ^reader; try

{

reader = XmlReader::Create("Monsters.xml"); while (reader->Read())

{

//...Process each node.

}

}

catch (XmlException ^e)

{

Console::WriteLine("\n\n\nSplitting XML Aborted with error: {0}", e->Message);

}

finally

{

if (reader->ReadState != ReadState::Closed)

{

reader->Close();

}

}