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

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

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

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

method. If you want to include comments, then you use the WriteComment() method. Once you’ve finished adding the XML document, you finish off with a WriteEndDocument() method. You might notice that the WriteEndDocument() method automatically ends any open elements.

writer->WriteComment("Add a weapon element"); writer->WriteStartElement("Weapon"); writer->WriteAttributeString("Number", "1"); writer->WriteAttributeString("Damage", "1d4"); writer->WriteString("Dagger"); writer->WriteEndElement();

Now that you have a new XML document, you must flush out any buffers and finally close the file so that some other process can access it. As you saw with the XmlReader class, you check the status of the file to make sure it even needs to be closed:

writer->Flush();

if (writer->WriteState != WriteState::Closed)

{

writer->Close();

}

Figure 13-3 shows Goblin.xml, the output of WriteXML.exe, displayed in the Visual Studio 2005 editor.

Figure 13-3. The generated Goblin.xml file

Updating an Existing XML File

You have many ways to update an XML file. Using a standard editor comes to mind. Another option, especially if you are working with a repetitive operation, is to read in the XML file using the XmlReader class, make your changes, and then write out the edited XML with XmlWriter.

A catch to using this method is that there is no backtracking with either the reader or the writer. Therefore, you must make all changes as the element or attribute becomes available or store them temporarily.

There isn’t anything new with this code. It simply isn’t obvious how it’s done. So here’s an example of how to update an XML file in a forward-only manner. In Listing 13-8, you’re adding the element <Encountered>False</Encountered> after the name of every monster.

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

579

Listing 13-8. Updating the XML Monster File

using namespace System; using namespace System::Xml;

void main()

{

XmlReader ^reader; XmlWriter ^writer; try

{

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

XmlWriterSettings ^settings = gcnew XmlWriterSettings(); settings->Indent = true;

settings->IndentChars = (" ");

writer = XmlWriter::Create("New_Monsters.xml", settings);

while (reader->Read())

{

switch (reader->NodeType)

{

case XmlNodeType::Comment: writer->WriteComment(reader->Value); break;

case XmlNodeType::Element: writer->WriteStartElement(reader->Name); writer->WriteAttributes(reader, false); if (reader->IsEmptyElement)

writer->WriteEndElement(); break;

case XmlNodeType::EndElement: writer->WriteEndElement();

// *** Add new Monster Element

if (reader->Name->Equals("Name"))

{

writer->WriteStartElement("Encountered"); writer->WriteString("False"); writer->WriteEndElement();

}

break;

case XmlNodeType::Text: writer->WriteString(reader->Value); break;

case XmlNodeType::XmlDeclaration: writer->WriteStartDocument(); break;

}

}

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

writer->Flush();

Console::WriteLine("Done");

}

catch (Exception ^e)

{

Console::WriteLine("XML Update Aborted -- {0}", e->Message);

}

finally

{

if (writer->WriteState != WriteState::Closed)

{

writer->Close();

}

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

{

reader->Close();

}

}

}

Notice that there is no “open for update” mode for either the reader or the writer, so you need to open an input and an output file:

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

XmlTWriter ^writer = XmlWriter::Create("New_Monsters.xml", settings);

After that, the code is standard XmlReader and XmlWriter logic. Basically, you read in each element, attribute, comment, and so on and then write them out again. When the end element of Name shows up, write it out and then dump out the new element:

while (reader->Read())

{

switch (reader->NodeType)

{

//...Other cases.

case XmlNodeType::EndElement: writer->WriteEndElement();

if (reader->Name->Equals("Name"))

{

writer->WriteStartElement("Encountered"); writer->WriteString("False"); writer->WriteEndElement();

}

break;

//...The remaining cases.

Figure 13-4 shows New_Monsters.xml, the output of UpdateXML.exe, displayed in the Visual Studio 2005 editor.

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

581

Figure 13-4. The generated New_Monsters.xml file

Working with DOM Trees

The Document Object Model (DOM) is a specification for how to store and manipulate XML documents in memory. This differs significantly from the forward-only access just discussed, because for that method only a single node of the XML document is in memory at any one time. Having the entire document in memory has some major advantages and a couple of significant disadvantages compared to forward-only access.

The most important advantage is that because the entire XML document is in memory, you have the ability to access any portion of the XML document at any time. This means you can read, search, write, change, and delete anywhere at any time in the document. Best of all, once you are through, you can dump the XML document back to disk with a single command.

The major disadvantages are that the DOM tree can use up a lot more memory than forwardonly access. especially if the XML document is large, and that there is often a delay as the DOM tree is loaded. Are these disadvantages significant? In most cases the answer is not really. Most computers have more than enough memory to handle all but the very largest XML documents (and when a document gets that large, the data should probably be in a database anyway). The slight delay is usually masked in the start-up of the application, and for the delay to be noticeable at all, the XML document needs to be quite sizable. (Again, when an XML document gets that large, it should probably be placed in a database.)

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

The core underlying class of the DOM tree is the abstract class XmlNode. You should be able to get comfortable quickly with XmlNode, as the classes derived from XmlNode have a close resemblance to the node types you worked with in the previous section. As you can see in Table 13-3, every type of node that is part of an XML document inherits from XmlNode. In fact, even the XmlDocument class is inherited from XmlNode.

Table 13-3. Classes Derived from XmlNode

Class

Description

XmlAttribute

Represents an attribute

XmlCDataSection

Represents a CDATA section

XmlCharacterData

Provides text manipulation methods that are used by several

 

inherited classes

XmlComment

Represents an XML comment

XmlDataDocument

Provides the ability to store, retrieve, and manipulate data

 

through a relational DataSet

XmlDeclaration

Represents the XML declaration node

XmlDocument

Represents an XML document

XmlDocumentFragment

Represents a fragment or hierarchical branch of the XML

 

document tree

XmlDocumentType

Represents the DTD

XmlElement

Represents an element

XmlEntity

Represents an entity declaration

XmlEntityReference

Represents an entity reference node

XmlLinkedNode

Provides the ability to get the node before and after the

 

current node

XmlNotation

Represents a notation declaration

XmlProcessingInstruction

Represents a processing instruction

XmlSignificantWhitespace

Represents white space between markup in a mixed content

 

mode or white space within an xml:space= 'preserve' scope

XmlText

Represents the text content of an element or attribute

XmlWhitespace

Represents white space in element content

 

 

Because it’s easier to visualize the XmlNode hierarchy than describe it in text, I’ve included the following illustration:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

583

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

You use the properties and the methods defined in the XmlNode class to navigate, manipulate, and remove the nodes of the DOM tree. Here are some of the more common XmlNode properties:

Attributes is an XmlAttributeCollection containing the attributes of the current node.

ChildNodes is an XmlNodeList containing all the child nodes of the current node.

FirstChild is an XmlNode of the first child of the current node, probably the XML declaration. If there is no first child node, then the value is null.

HasChildNodes is a Boolean that is true if the node has any children; otherwise, it is false.

InnerText is a String concatenation of the value of the current node and all of its children.

InnerXml is a String representing the markup of the children of the current node. Setting this property replaces all the children of the current node.

IsReadOnly is a Boolean that is true if the node is read-only; otherwise, it is false.

Item is an XmlElement child of the current node specified by name.

LastChild is an XmlNode of the last child of the current node.

LocalName is a String representing the name of the current node without the namespace prefix.

Name is a String representing the qualified name of the current node.

NextSibling is the XmlNode with the same parent immediately following the current node. It has a value of null if no subsequent sibling exists.

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

OuterXml is a String representing the markup of the current node and of the children of the current node.

OwnerDocument is the XmlDocument of which the current node belongs.

ParentNode is the XmlNode of the parent of the current node.

PreviousSibling is the XmlNode with the same parent immediately before the current node. It has a value of null if no prior sibling exists.

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

Value is a String representing the value of the current node.

As mentioned previously, XmlNode has methods. Here are some of the more common ones:

AppendChild() adds a child to the end of the list of children for the current node.

CloneNode() creates a duplicate of the current node.

CreateNavigator() creates an XPathNavigator.

InsertAfter() inserts a node immediately after the current node.

InsertBefore() inserts a node immediately before the current node.

PrependChild() adds a child at the beginning of the list of children for the current node.

RemoveAll() removes all children and/or attributes for the current node.

RemoveChild() removes the specified child node.

ReplaceChild() replaces the specified child node.

SelectNodes() selects a list of nodes that matches a specified XPath expression.

SelectSingleNode() selects the first node that matches a specified XPath expression.

WriteContentTo() saves all the children of the XmlDocument to an XmlWriter.

WriteTo() saves the XmlDocument to an XmlWriter.

XmlNodes are placed in an XmlNodeList. This list is ordered and supports indexed as well as enumerated access. Any changes that you make to the XmlNodes in the DOM tree are immediately reflected in the XmlNodeList in which the XmlNodes reside. You can find the root of all XmlNodeLists in the DocumentElement property of the XmlDocument class.

The starting point of working with DOM trees is the XmlDocument class. Not only do you use this class to load and save the XML document to and from disk, but you also use it to query the DOM tree and create nodes to be added to the tree. As you might have noticed in Table 13-3, XmlDocument inherits from XmlNode, so the XmlDocument class has all the XmlNode class’s properties and methods. Here are some of the more common properties unique to XmlDocument:

DocumentElement is an XmlElement representing the root element of the document.

DocumentType is an XmlDocumentType containing the DocumentType or DOCTYPE declaration if the document has one.

PreserveWhitespace is a Boolean that is true if white space is to be preserved; otherwise, it is false.

As you can see, the XmlDocument class provides quite a bit of additional functionality over the XmlNode class. The following are some of the XmlDocument class’s unique methods:

CreateAttribute() creates an XmlAttribute.

CreateCDataSection() creates an XmlCDataSection.

CreateComment() creates an XmlComment.

CreateDocumentFragment() creates an XmlDocumentFragment.

CreateDocumentType() creates an XmlDocumentType.

CreateElement() creates an XmlElement.

CreateEntityReference() creates an XmlEntityReference.

CreateNode() creates an XmlNode.

CreateTextNode() creates an XmlText.

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

585

CreateXmlDeclaration() creates an XmlDeclaration.

GetElementById() gets an element based on a specified ID.

GetElementsByTagName() gets an XmlNodeList of all elements that match the specified tag.

ImportNode() imports a node for another XmlDocument.

Load() loads into the XmlDocument a File, Stream, TextReader, or XmlReader.

LoadXml() loads into the XmlDocument a String.

ReadNode() creates an XmlNode based on the current position of an XmlReader.

Save() saves the XmlDocument to a specified filename, Stream, TextWriter, or XmlWriter.

Reading a DOM Tree

You have many different ways of navigating through a DOM tree. You’ll start out by using only the basic methods found in XmlDocument, XmlNode, and XmlNodeList. Later you’ll look at an easier way of navigating using XPaths.

Because the DOM is stored in a tree in memory, it’s a good candidate for navigating via recursion. The example in Listing 13-9 demonstrates an implementation of recursively following the tree branch and dumping the node information it passed along the way. You dump the tree to the system console.

Listing 13-9. Reading a DOM Tree Recursively

using namespace System; using namespace System::Xml;

String ^indent(int depth)

{

String ^ind = "";

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

}

void Navigate(XmlNode ^node, int depth)

{

if (node == nullptr) return;

Console::WriteLine("{0}: Name='{1}' Value='{2}'", String::Concat(indent(depth),node->NodeType.ToString()), node->Name, node->Value);

if (node->Attributes != nullptr)

{

for (int i = 0; i < node->Attributes->Count; i++)

{

Console::WriteLine("{0}Attribute: Name='{1}' Value='{2}'", indent(depth+1),node->Attributes[i]->Name, node->Attributes[i]->Value);

}

}

Navigate(node->FirstChild, depth+1); Navigate(node->NextSibling, depth);

}

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

void main()

{

XmlDocument ^doc = gcnew XmlDocument();

try

{

XmlReader ^reader = XmlReader::Create("Monsters.xml"); doc->Load(reader);

reader->Close();

XmlNode ^node = doc->FirstChild; // I want the Xml Declaration

// Recursive navigation of the DOM tree Navigate(node, 0);

}

catch (Exception ^e)

{

Console::WriteLine("Error Occurred: {0}", e->Message);

}

}

As I stated before, you process all XML documents within an exception try block because every XML method in the .NET Framework class library can throw an exception.

Before you start reading the DOM tree, you need to load it. First, you create an XmlDocument to hold the tree. You do this using a standard constructor:

XmlDocument ^doc = gcnew XmlDocument();

Then you load the XML document into the XmlDocument. It is possible to pass the name of the XML file directly into the Load() method, which I think is a little easier. But, if you do it the following way, make sure you close the file after the load is complete, because the file resource remains open longer than it needs to be. Plus, if you try to write to the file, it will throw an exception because the file is already open.

XmlReader ^reader = XmlReader::Create("Monsters.xml"); doc->Load(reader);

reader->Close();

In the previous example, I call the XmlDocument class’s FirstChild() method instead of the DocumentElement() method because I want to start reading the XML document at the XML declaration and not the first element of the document.

XmlNode ^node = doc->FirstChild; // I want the Xml Declaration

Finally, you call a simple recursive method to navigate the tree. The first thing this method does is check to make sure that you have not already reached the end of the current branch of the tree:

if (node == nullptr) return;

Then it dumps to the console the current node’s type, name, and value. Notice that I use the little trick I mentioned in Chapter 3 to display the enum class’s (in this case, the NodeType’s) String name:

Console::WriteLine("{0}: Name='{1}' Value='{2}'", String::Concat(indent(depth), node->NodeType.ToString()), node->Name, node->Value);

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

587

The method then checks to see if the element has any attributes. If it does, it then iterates through them, dumping each to the console as it goes:

if (node->Attributes != nullptr)

{

for (int i = 0; i < node->Attributes->Count; i++)

{

Console::WriteLine("{0}Attribute: Name='{1}' Value='{2}'", indent(depth+1),

node->Attributes[i]->Name, node->Attributes[i]->Value));

}

}

The last thing the method does is call itself to navigate down through its children, and then it calls itself to navigate through its siblings:

Navigate(node->FirstChild, depth+1);

Navigate(node->NextSibling, depth);

Figure 13-5 shows the resulting console dump for ReadXMLDOM.exe of all the nodes and attributes that make up the monster DOM tree.

Figure 13-5. The console dump of the monster DOM tree