** 设计 ** ** XmlReadWriter类 **
** ** 如前面所说, XML reader和Writer是各自独立工作的:reader只读,writer只写。假设你的应用程序要管理冗长的XML文档,且该文档有不确定的数据。Reader提供了一个很好的方法去读该文档的内容。另一方面,Writer是一个非常有用的用于创建XML文档片断工具,但是如果你想要它即能读,又能写,那么你就要用XMLDOM了。如果实际的XML文档非常庞大,又会出现了一个问题,什么问题呢?是不是把这个XML文档全部加载到内存中,然后进行读和写呢?让我们先看一下怎么样建立一个混合的流分析器用于分析大型的XMLDOM。
像一般的只读操作一样,用普通的 XML reader 去顺序的访问节点。不同的是,在读的同时你可以用 XML writer 改变属性值以及节点的内容。你用 reader 去读源文件中的每个节点,后台的 writer 创建该节点的一个拷贝。在这个拷贝中,你可以增加一些新的节点,忽略或者编辑其它的一些节点,还可以编辑属性的值。当你完成修改后,你就用新的文档替换旧的文档。
一个简单有效的办法是从只读流中拷贝节点对象到 write 流中,这种方法可以用 XmlTextWriter 类中的两个方法: WriteAttributes 方法和 WriteNode 方法。 WriteAttributes 方法读取当前 reader 中选中的节点的所有有效的属性,然后把属性当作一个单独的 string 拷贝到当前的输出流中。同样的, WriteNode 方法用类似的方法处理除属性节点外的其它类型的节点。图十所示的代码片断演示了怎么用上述的两个方法创建一个源 XML 文档的拷贝 , 有选择的修改某些节点。 XML 树从树根开始被访问,但只输出了除属性节点类型以外的其它类型的节点。你可以把 Reader 和 Writer 整合在一个新的类中,设计一个新的接口,使它能读写流及访问属性和节点。
** Figure 10 ** Using the WriteNode Method
XmlTextReader reader = new XmlTextReader(inputFile);
XmlTextWriter writer = new XmlTextWriter(outputFile);
// 配置 reader 和 writer
writer.Formatting = Formatting.Indented;
reader.MoveToContent();
// Write 根节点
writer.WriteStartElement(reader.LocalName);
// Read and output every other node
int i=0;
while(reader.Read())
{
if (i % 2)
writer.WriteNode(reader, false);
i++;
}
// Close the root
writer.WriteEndElement();
// Close reader and writer
writer.Close();
reader.Close();
我的 XmlTextReadWriter 类并没有从 XmlReader 或者 XmlWriter 类中继承。取而代之的是另外两个类,一个是基于只读流 (stream) 的操作类 , 另一个是基于只写流的操作类。 XmlTextReadWriter 类的方法用 Reader 对象读数据,写入到 Writer 对象。为了适应不同的需求,内部的 Reader 和 Writer 对象分别通过只读的 Reader 和 Writer 属性公开。图十一列出了该类的一些方法:
** Figure 11 ** XmlTextReadWriter Class Methods
Method
|
Description
---|---
AddAttributeChange
|
Caches all the information needed to perform a change on a node attribute. All the changes cached through this method are processed during a successive call to WriteAttributes.
Read
|
Simple wrapper around the internal reader's Read method.
WriteAttributes
|
Specialized version of the writer's WriteAttributes method, writes out all the attributes for the given node, taking into account all the changes cached through the AddAttributeChange method.
WriteEndDocument
|
Terminates the current document in the writer and closes both the reader and the writer.
WriteStartDocument
|
Prepares the internal writer to output the document and add a default comment text and the standard XML prolog.
这个新类有一个 Read 方法,它是对 Reader 的 read 方法的一个简单的封装。另外,它提供了 WriterStartDocument 和 WriteEndDocument 方法。它们分别初始化 / 释放 (finalize) 了内部 Reader 和 writer 对象,还处理所有 I/O 操作。在循环读节点的同时,我们就可以直接的修改节点。出于性能的原因,要修改属性必须先用 AddAttributeChange 方法声明。对一个节点的属性所作的所有修改都会存放在一个临时的表中,最后,通过调用 WriteAttribute 方法提交修改,清除临时表。
图十二所示的代码演示了客户端用 XmlTextReadWriter 类在读操作的同时修改属性值的优势。在本期的 msdn 中提供了 XmlTextReadWriter 类的 C# 和 VB 源代码下载(见本文开头提供的链接)。
** Figure 12 ** Changing Attribute Values
private void ApplyChanges(string nodeName, string attribName,
string oldVal, string newVal)
{
XmlTextReadWriter rw = new XmlTextReadWriter(InputFileName.Text,
OutputFileName.Text);
rw.WriteStartDocument(true, CommentText.Text);
// 手工修改根节点
rw.Writer.WriteStartElement(rw.Reader.LocalName);
// 开始修改属性
// ( 可以修改更多节点的属性 )
rw.AddAttributeChange(nodeName, attribName, oldVal, newVal);
// 循环处理文档
while(rw.Read())
{
switch(rw.NodeType)
{
case XmlNodeType.Element:
rw.Writer.WriteStartElement(rw.Reader.LocalName);
if (nodeName == rw.Reader.LocalName)
// 修改属性
rw.WriteAttributes(nodeName);
else
// deep copy
rw.Writer.WriteAttributes(rw.Reader, false);
if (rw.Reader.IsEmptyElement)
rw.Writer.WriteEndElement();
break;
}
}
// Close the root tag
rw.Writer.WriteEndElement();
// Close the document and any internal resources
rw.WriteEndDocument();
}
XmlTextReadWriter 类不仅可以读 XML 文档,也可以写 XML 文档。你可以它来读 XML 文档的内容,如果需要,你还可以用它来做一些基本的更新操作。基本的更新操作在这里是指修改某个已存在的属性的值或者某个节点的内容,又或者是增加一个新的属性或节点。对于更复杂的操作,最好还是用 XMLDOM 分析器。
** 总结 **
Reader 和 Writer 是 .NET Framework 中处理 XML 数据的根本。它们提供了对所有 XML 数据访问功能的原始的 API 。 Reader 像一个新的分析器类,它即有 XMLDOM 的强大,又有 SAX 的快速简单。 Writer 为简单的创建 XML 文档而设计。虽然 Reader 和 Writer 都是 .NET Framework 中的一小块,但是它们是相互独立的 API 。在本文中,我们只讨论了怎么样用 Reader 和 Writer 完成一些主要的工作 , 介绍了验证分析器的原理机制,并把 Reader 和 writer 整合在一个单独的类中。上述所有的这些类都是轻量级的,类似于游标式的 XMLDOM 分析器。