基于流的 XML 处理
.NET Framework 允许你使用 System.Xml 命名空间(以及它的子命名空间)中的一组类来操作 XML 数据。有两个基于流的类:XmlTextReader 和 XmlTextWriter ,它们对简单的 XML 处理非常有效。
.NET 通过两种方式把 XML 数据写入文件:
下面的示例演示如何使用 XmlTextWriter 创建一个格式良好的 XML 文件:
private void WriteXml()
{
string xmlFile = Server.MapPath("DvdList.xml");
// 第二个参数可传递一个 System.Text.Encoding 编码
// 传递空引用表示使用标准的 UTF-8 编码
XmlTextWriter writer = new XmlTextWriter(xmlFile, null);
// Formatting.Indented 表示使用缩进
// Formatting.None 表示不应用特殊的格式(默认)
// Indentation 表示缩进 3 空格
writer.Formatting = Formatting.Indented;
writer.Indentation = 3;
// 编写版本为“1.0”的 XML 声明
writer.WriteStartDocument();
// 写出包含指定文本的注释 <!--...-->
writer.WriteComment("Creaded @ " + DateTime.Now.ToString());
// 写出具有指定的本地名称的开始标记
writer.WriteStartElement("DvdList");
writer.WriteStartElement("DVD");
writer.WriteAttributeString("ID", "1");
writer.WriteAttributeString("Category", "Science Fiction");
// 下面这些元素不包含子元素,因此不需要使用 WriteStartElement()
writer.WriteElementString("Title", "The Matrix");
writer.WriteElementString("Director", "Larry Wachowski");
writer.WriteElementString("Price", "18.74");
// 下面这个 Starring 包含一个或多个元素
// 所以要使用 WriteStartElement()
writer.WriteStartElement("Starring");
writer.WriteElementString("Star", "Keanu Reeves");
writer.WriteElementString("Star", "Laurence Fishburne");
// 接着以相反的顺序关闭所有打开的标签
writer.WriteEndElement();
writer.WriteEndElement();
// 再创建另一个 DVD 元素
writer.WriteStartElement("DVD");
writer.WriteAttributeString("ID", "2");
writer.WriteAttributeString("Category", "Drama");
writer.WriteElementString("Title", "Forrest Gump");
writer.WriteElementString("Director", "Robert Zemeckis");
writer.WriteElementString("Price", "23.99");
writer.WriteStartElement("Starring");
writer.WriteElementString("Star", "Tom Hanks");
writer.WriteElementString("Star", "Robin Wright");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.Close();
}
如果要给它一个唯一的 XML 命名空间标识 XML 文档,那么元素也需要放到这个命名空间里。如果要这么做,首先要使用 WriteAttributeString()输出一个 xmlns 特性,通常会把这个特性加到文档的根元素或第一个使用命名空间的元素上。其次,元素名称必须使用命名空间前缀限定,WriteStartElement()方法的重载版本可以达到这一目的。
基于流的方法减少了内存负担,但如果对 XML 文档执行耗时任务或为了不影响别人访问这个XML文档,还是选择在内存中操作的方式较好。
XmlTextReader 读取 XML 文件固然简单,但也是最不灵活的。文件以顺序读取,不能够像处理内存中 XML 那样自由的移动到父、子、兄弟节点。
下面的代码加载源文件,然后开始每次移动一个文档节点的循环:
private string ReadXml()
{
string xmlFile = Server.MapPath("DvdList.xml");
XmlTextReader reader = new XmlTextReader(xmlFile);
StringBuilder str = new StringBuilder();
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
str.Append("Element: <b>").Append(reader.Name).Append("</b><br />");
break;
case XmlNodeType.Text:
str.Append(" - Value: <b>").Append(reader.Value).Append("</b><br />");
break;
case XmlNodeType.XmlDeclaration:
str.Append("Xml Declaration: <b>");
str.Append(reader.Name).Append(" ").Append(reader.Value).Append("</b><br />");
break;
}
if (reader.AttributeCount > 0)
{
while (reader.MoveToNextAttribute())
{
str.Append(" - Attribute: <b>").Append(reader.Name);
str.Append("</b> Value: <b>").Append(reader.Value).Append("</b><br />");
}
}
}
reader.Close();
return str.ToString();
}
使用 XmlTextReader 时,必须尽可能快的结束任务并关闭阅读器,因为它在文件上有一个锁。
下面这段代码以更直观的方式从 DVD 列表中获取数据:
private string ReadXml()
{
string xmlFile = Server.MapPath("DvdList.xml");
XmlTextReader reader = new XmlTextReader(xmlFile);
StringBuilder str = new StringBuilder();
reader.ReadStartElement("DvdList");
while (reader.Read())
{
if (reader.Name == "DVD" && reader.NodeType == XmlNodeType.Element)
{
reader.ReadStartElement("DVD");
str.Append("<ul><b>");
str.Append(reader.ReadElementString("Title"));
str.Append("</b><li>");
str.Append(reader.ReadElementString("Director"));
str.Append("</li><li>");
str.Append(string.Format("{0:C}",Decimal.Parse(reader.ReadElementString("Price"))));
str.Append("</li></ul>");
}
}
reader.Close();
return str.ToString();
}