XmlTextWriter
类
用在本节中的方法创建
XML
文档显然并不困难。多年以来,开发者都是通过在缓存在连接一些字符串,连接好以后再把缓存中字符串输出到文件的方式来创建
XML
文档。但是以这种方式创建
XML
文档的方法只有在你保证字符串中不存在任何细小的错误的时候才有效。
.NET Framework
通过用
XMLwriter
提供了更好的创建
XML
文档的方法。
XML Writer 类以只前( forward-only )的方式输出 XML 数据到流或者文件中。更重要的是, XML Writer 在设计时就保证所有的 XML 数据都符合 W3C XML 1.0 推荐规范,你甚至不用担心忘记写闭标签,因为 XML Writer 会帮你写。 XmlWriter 是所有 XML writer 的抽象基类。 .NET Framework 只提供唯一的一个 writer 类 ----XmlTextWriter 类。
我们先来看看 XML writers 和旧的 writers 的不同点,下面的代码保存了一个 string 型的数组:
StringBuilder sb = new StringBuilder("");
sb.Append("");
foreach(string s in theArray) {
sb.Append("
sb.Append(s);
sb.Append("/"/>");
}
sb.Append("");
代码通过循环取出数据中的元素,写好标签文本并把它们累加到一个string中。代码保证输出的内容是格式良好的并且注意了新行的缩进,及支持命名空间。当创建的文档结构比较简单时,这种方法可能不会有错误。然而,当你要支持处理指令,命名空间,缩进,格式化以及实体的时候,代码的数量就成指数级增长,出错的可能性也随之增长。
XML writer写方法功能对应每个可能的XML节点类型,它使创建xml文档的过程更符合逻辑、更少的信赖于繁琐的标记语言。图六演示了怎么样用XmlTextWriter类的方法来连接一个string数据。代码很简洁,用XML writer的代码更容易读、结构更好。
Figure 6 Serializing a String Array
void CreateXmlFileUsingWriters(String[] theArray, string filename)
{
// Open the XML writer (用默认的字符集)
XmlTextWriter xmlw = new XmlTextWriter(filename, null);
xmlw.Formatting = Formatting.Indented;
xmlw.WriteStartDocument();
xmlw.WriteStartElement("array");
foreach(string s in theArray)
{
xmlw.WriteStartElement("element");
xmlw.WriteAttributeString("value", s);
xmlw.WriteEndElement();
}
xmlw.WriteEndDocument();
// Close the writer
xmlw.Close();
}
然而XML writer并不是魔术师----它不能修复输入的错误。XML writer不会检查元素名和属性名是否有效,也不保证被用的任何的Unicode字符集适合当前架构的编码集。如上所述,为了避免输出错误,必须要杜绝非XML字符。但是writer没有提供这种方法。
另外,当创建一个属性节点时,Writer不会检验属性节点的名称是否与已存在的元素节点的名称相同。最后,XmlWriter类不是一个带验证的Writer类,也不保证输出是否符合schema或者DTD。在.NET Framework中带验证的writer类目前来说还没有提供。但是在我写的《Applied XML Programming for Microsoft .NET (Microsoft Press®, 2002)》书中,我自己写了一个带验证的Writer组件。你可以到下面的网址去下载源码:http://www.microsoft.com/MSPress/books/6235.asp.
图七列出了XML writer的一些状态值(state)。这些值都源于WriteState枚举类。当你创建一个Writer,它的初始状态为Start,表示你将要配置该对象,实际上writer没有开始。下一个状态是Prolog,该状态是当你调用WriteStartDocument方法开始工作的时候设置的。然后,状态的转换就取决于你的写的文档及文档的内容了。Prolog状态一直保留到当你增加一个非元素节点时,例如注释元素,处理指令及文档类型。当第一个节点也就是根节点写完后,状态就变为Element。当你调用WriterStartAtribute方法时状态转换为Attribute,而不是当你调用WriteAtributeString方法写属性时转换为该状态。如果那样的话,状态应该是Element。当你写一个闭标签(>)时,状态会转换成Content。当你写完文档后,调用WriteEndDocument方法,状态就会返回为Start,直到你开始写另一个文档或者把Writer关掉。
XML Writer 类以只前( forward-only )的方式输出 XML 数据到流或者文件中。更重要的是, XML Writer 在设计时就保证所有的 XML 数据都符合 W3C XML 1.0 推荐规范,你甚至不用担心忘记写闭标签,因为 XML Writer 会帮你写。 XmlWriter 是所有 XML writer 的抽象基类。 .NET Framework 只提供唯一的一个 writer 类 ----XmlTextWriter 类。
我们先来看看 XML writers 和旧的 writers 的不同点,下面的代码保存了一个 string 型的数组:
StringBuilder sb = new StringBuilder("");
sb.Append("");
foreach(string s in theArray) {
sb.Append("
sb.Append(s);
sb.Append("/"/>");
}
sb.Append("");
代码通过循环取出数据中的元素,写好标签文本并把它们累加到一个string中。代码保证输出的内容是格式良好的并且注意了新行的缩进,及支持命名空间。当创建的文档结构比较简单时,这种方法可能不会有错误。然而,当你要支持处理指令,命名空间,缩进,格式化以及实体的时候,代码的数量就成指数级增长,出错的可能性也随之增长。
XML writer写方法功能对应每个可能的XML节点类型,它使创建xml文档的过程更符合逻辑、更少的信赖于繁琐的标记语言。图六演示了怎么样用XmlTextWriter类的方法来连接一个string数据。代码很简洁,用XML writer的代码更容易读、结构更好。
Figure 6 Serializing a String Array
void CreateXmlFileUsingWriters(String[] theArray, string filename)
{
// Open the XML writer (用默认的字符集)
XmlTextWriter xmlw = new XmlTextWriter(filename, null);
xmlw.Formatting = Formatting.Indented;
xmlw.WriteStartDocument();
xmlw.WriteStartElement("array");
foreach(string s in theArray)
{
xmlw.WriteStartElement("element");
xmlw.WriteAttributeString("value", s);
xmlw.WriteEndElement();
}
xmlw.WriteEndDocument();
// Close the writer
xmlw.Close();
}
然而XML writer并不是魔术师----它不能修复输入的错误。XML writer不会检查元素名和属性名是否有效,也不保证被用的任何的Unicode字符集适合当前架构的编码集。如上所述,为了避免输出错误,必须要杜绝非XML字符。但是writer没有提供这种方法。
另外,当创建一个属性节点时,Writer不会检验属性节点的名称是否与已存在的元素节点的名称相同。最后,XmlWriter类不是一个带验证的Writer类,也不保证输出是否符合schema或者DTD。在.NET Framework中带验证的writer类目前来说还没有提供。但是在我写的《Applied XML Programming for Microsoft .NET (Microsoft Press®, 2002)》书中,我自己写了一个带验证的Writer组件。你可以到下面的网址去下载源码:http://www.microsoft.com/MSPress/books/6235.asp.
图七列出了XML writer的一些状态值(state)。这些值都源于WriteState枚举类。当你创建一个Writer,它的初始状态为Start,表示你将要配置该对象,实际上writer没有开始。下一个状态是Prolog,该状态是当你调用WriteStartDocument方法开始工作的时候设置的。然后,状态的转换就取决于你的写的文档及文档的内容了。Prolog状态一直保留到当你增加一个非元素节点时,例如注释元素,处理指令及文档类型。当第一个节点也就是根节点写完后,状态就变为Element。当你调用WriterStartAtribute方法时状态转换为Attribute,而不是当你调用WriteAtributeString方法写属性时转换为该状态。如果那样的话,状态应该是Element。当你写一个闭标签(>)时,状态会转换成Content。当你写完文档后,调用WriteEndDocument方法,状态就会返回为Start,直到你开始写另一个文档或者把Writer关掉。
Figure 7 States for XML Writer
State
Description
Attribute
The writer enters this state when an attribute is being written
Closed
The Close method has been called and the writer is no longer available for writing operations
Content
The writer enters this state when the content of a node is being written
Element
The writer enters this state when an element start tag is being written
Prolog
The writer is writing the prolog of a well-formed XML 1.0 document
Start
The writer is in an initial state, awaiting for a write call to be issued
Writer 把输出文本存在内部的一个缓冲区内。一般情况下,缓冲区会被刷新或者被清除,当 Writer 被关闭前 XML 文本应该要写出。在任何时你都可以通过调用 Flush 方法清空缓冲区,把当前的内容写到流中(通过 BaseStream属性 暴露流),然后释放部分占用的内存, Writer 仍保持为打开状态( open state ),可以继续操作。注意,虽然写了部分的文档内容,但是在 Writer 没有关闭前其它的程序是不能处理该文档的。
可以用两种方法来写 属性 节点。第一种方法是用 WriteStartAtribute 方法去创建一个新的 属性 节点,更新 Writer 的状态。接着用 WriteString 方法设置 属性 值。写完后,用 WriteEndElement 方法结束该节点。另外,你也可以用 WriteAttributeString 方法去创建新的 属性 节点,当 writerr 的状态为 Element 时, WriterAttributeString 开始工作,它单独创建一个 属性 。同样的, WriteStartElement 方法写节点的开始标签( < ),然后你可以随意的设置节点的 属性 和文本内容。元素节点的闭标签都带 ”/ >” 。如果想写闭标签可以用 WriteFullEndElement 方法来写。
应该避免传送给写方法的文本中包含敏感的标记字符,例如小于号( < )。用 WriteRaw 方法写入流的字符串不会被解析,我们可以用它来对 xml 文档写入特殊的字符串。下面的两行代码,第一行输出的是 ”<” ,第二行输出 ”<”:
writer.WriteString("<");
writer.WriteRaw("<");
State
Description
Attribute
The writer enters this state when an attribute is being written
Closed
The Close method has been called and the writer is no longer available for writing operations
Content
The writer enters this state when the content of a node is being written
Element
The writer enters this state when an element start tag is being written
Prolog
The writer is writing the prolog of a well-formed XML 1.0 document
Start
The writer is in an initial state, awaiting for a write call to be issued
Writer 把输出文本存在内部的一个缓冲区内。一般情况下,缓冲区会被刷新或者被清除,当 Writer 被关闭前 XML 文本应该要写出。在任何时你都可以通过调用 Flush 方法清空缓冲区,把当前的内容写到流中(通过 BaseStream属性 暴露流),然后释放部分占用的内存, Writer 仍保持为打开状态( open state ),可以继续操作。注意,虽然写了部分的文档内容,但是在 Writer 没有关闭前其它的程序是不能处理该文档的。
可以用两种方法来写 属性 节点。第一种方法是用 WriteStartAtribute 方法去创建一个新的 属性 节点,更新 Writer 的状态。接着用 WriteString 方法设置 属性 值。写完后,用 WriteEndElement 方法结束该节点。另外,你也可以用 WriteAttributeString 方法去创建新的 属性 节点,当 writerr 的状态为 Element 时, WriterAttributeString 开始工作,它单独创建一个 属性 。同样的, WriteStartElement 方法写节点的开始标签( < ),然后你可以随意的设置节点的 属性 和文本内容。元素节点的闭标签都带 ”/ >” 。如果想写闭标签可以用 WriteFullEndElement 方法来写。
应该避免传送给写方法的文本中包含敏感的标记字符,例如小于号( < )。用 WriteRaw 方法写入流的字符串不会被解析,我们可以用它来对 xml 文档写入特殊的字符串。下面的两行代码,第一行输出的是 ”<” ,第二行输出 ”<”:
writer.WriteString("<");
writer.WriteRaw("<");
读写流
有趣的是, reader( 阅读器 ) 和 writer 类提供了基于 Base64 和 BinHex 编码的读写数据流的方法。 WriteBase64 和 WriteBinHex 方法的功能与其它的写方法的功能存在着细微的差别。它们都是基于流的,这两个方法的功能像一个 byte 数组而不是一个 string 。下面的代码首先把一个 string 转换成一个 byte 数组,然后把它们写成一个 Base64 编码流。 Encoding 类的 GetBytes 静态方法完成转换的任务:
writer.WriteBase64(
Encoding.Unicode.GetBytes(buf),
0, buf.Length*2);
图八中代码演示了把一个string数据转换为Base64 编码的
XML流。图九是输出的结果。
Figure 8 Persisting a String Array as Base64
using System;
using System.Text;
using System.IO;
using System.Xml;
class MyBase64Array
{
public static void Main(String[] args)
{
string outputFileName = "test64.xml";
if (args.Length > 0)
outputFileName = args[0]; // file name
// 把数组转换成 XML
String[] theArray = {"Rome", "New York", "Sydney", "Stockholm",
"Paris"};
CreateOutput(theArray, outputFileName);
return;
}
private static void CreateOutput(string[] theArray, string filename)
{
// 打开 XML writer
XmlTextWriter xmlw = new XmlTextWriter(filename, null);
//使子元素根据 Indentation 和 IndentChar 设置缩进。此选项只对元素内容进行缩进
xmlw.Formatting = Formatting.Indented;
//书写版本为“1.0”的 XML 声明
xmlw.WriteStartDocument();
//写出包含指定文本的注释 。
xmlw.WriteComment("Array to Base64 XML");
//开始写出array节点
xmlw.WriteStartElement("array");
//写出具有指定的前缀、本地名称、命名空间 URI 和值的 属性循环的写入array的子节点
foreach(string s in theArray)
{
//写出指定的开始标记并将其与给定的命名空间和前缀关联起来
xmlw.WriteStartElement("x", "element", null);
//把S转换成byte[]数组, 并把byte[]数组编码为 Base64 并写出结果文本,要写入的字节数为s总长度的2倍,一个string占的字节数是2字节。
xmlw.WriteBase64(Encoding.Unicode.GetBytes(s), 0, s.Length*2);
//关闭子节点
xmlw.WriteEndElement();
}
//关闭根节点,只有两级
xmlw.WriteEndDocument();
// 关闭writer
xmlw.Close();
// 读出写入的内容
XmlTextReader reader = new XmlTextReader(filname);
while(reader.Read())
{
//获取节点名为element的节点
if (reader.LocalName == "element")
{
byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
Console.WriteLine(buf.Substring(0,n));
}
}
reader.Close();
}
}
xmlw.WriteAttributeString("xmlns", "x", null, "dinoe:msdn-mag");
//
Figure 8 Persisting a String Array as Base64
using System;
using System.Text;
using System.IO;
using System.Xml;
class MyBase64Array
{
public static void Main(String[] args)
{
string outputFileName = "test64.xml";
if (args.Length > 0)
outputFileName = args[0]; // file name
// 把数组转换成 XML
String[] theArray = {"Rome", "New York", "Sydney", "Stockholm",
"Paris"};
CreateOutput(theArray, outputFileName);
return;
}
private static void CreateOutput(string[] theArray, string filename)
{
// 打开 XML writer
XmlTextWriter xmlw = new XmlTextWriter(filename, null);
//使子元素根据 Indentation 和 IndentChar 设置缩进。此选项只对元素内容进行缩进
xmlw.Formatting = Formatting.Indented;
//书写版本为“1.0”的 XML 声明
xmlw.WriteStartDocument();
//写出包含指定文本的注释 。
xmlw.WriteComment("Array to Base64 XML");
//开始写出array节点
xmlw.WriteStartElement("array");
//写出具有指定的前缀、本地名称、命名空间 URI 和值的 属性循环的写入array的子节点
foreach(string s in theArray)
{
//写出指定的开始标记并将其与给定的命名空间和前缀关联起来
xmlw.WriteStartElement("x", "element", null);
//把S转换成byte[]数组, 并把byte[]数组编码为 Base64 并写出结果文本,要写入的字节数为s总长度的2倍,一个string占的字节数是2字节。
xmlw.WriteBase64(Encoding.Unicode.GetBytes(s), 0, s.Length*2);
//关闭子节点
xmlw.WriteEndElement();
}
//关闭根节点,只有两级
xmlw.WriteEndDocument();
// 关闭writer
xmlw.Close();
// 读出写入的内容
XmlTextReader reader = new XmlTextReader(filname);
while(reader.Read())
{
//获取节点名为element的节点
if (reader.LocalName == "element")
{
byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
Console.WriteLine(buf.Substring(0,n));
}
}
reader.Close();
}
}
xmlw.WriteAttributeString("xmlns", "x", null, "dinoe:msdn-mag");
//
Figure 9 String Array in Internet Explorer
Reader类有专门的解释Base64和BinHex编码流的方法。下面的代码片断演示了怎么样用XmlTextReader类的ReadBase64方法解析用Base64和BinHex编码集创建的文档。
XmlTextReader reader = new XmlTextReader(filename);
while(reader.Read()) {
if (reader.LocalName == "element") {
byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
Console.WriteLine(buf.Substring(0,n));
}
}
reader.Close();
从byte型转换成string型是通过Encoding类的GetString方法实现的。尽管我只介绍了基于Base64编码集的代码,但是可以简单的用BinHex替换方法名就可以实现读基于BinHex编码的节点内容(用ReadBinHex方法)。这个技巧也可以用于读任何用byte数据形式表示的二进制数据,尤其是image类型的数据。
Reader类有专门的解释Base64和BinHex编码流的方法。下面的代码片断演示了怎么样用XmlTextReader类的ReadBase64方法解析用Base64和BinHex编码集创建的文档。
XmlTextReader reader = new XmlTextReader(filename);
while(reader.Read()) {
if (reader.LocalName == "element") {
byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
Console.WriteLine(buf.Substring(0,n));
}
}
reader.Close();
从byte型转换成string型是通过Encoding类的GetString方法实现的。尽管我只介绍了基于Base64编码集的代码,但是可以简单的用BinHex替换方法名就可以实现读基于BinHex编码的节点内容(用ReadBinHex方法)。这个技巧也可以用于读任何用byte数据形式表示的二进制数据,尤其是image类型的数据。
设计
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 方法提交修改,清除临时表。
像一般的只读操作一样,用普通的 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 分析器。
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 分析器。