XML文件操作指南
转自:http://bbs.ibox.com.cn/Message/2104/2103812.html
一、XML简介
XML的全名是eXtensible Markup Language(可以扩展的标记语言),它的语法类似HTML,都是用标签来描述数据。HTML的标签是固定的,我们只能使用、不能修改; XML则不同,它没有预先定义好的标签可以使用,而是依据设计上的需要,自行定义标签。XML是一个元语言,根据不同的行业和语义,由它可以派生出许许多多的协议和规范。
XML主要提供了两种对象模型。分别是:
(1)文档对象模型(DOM)类是XML文档的内存中表示形式。DOM 使你能够以编程方式读取、操作和修改XML文档。XmlReader类也读取XML,但它提供非缓存的只进、只读访问。这意味着使用 XmlReader无法编辑属性值或元素内容,或无法插入和移除节点。编辑是 DOM 的主要功能。XML数据在内存中表示是常见的结构化方法,尽管实际的XML数据在文件中时或从另一个对象传入时以线性方式存储。
(2)架构对象模型(SOM)在System.Xml.Schema命名空间中提供了一组类,该命名空间对应于并完全遵守WWW 联合会(W3C) XML Schema Recommendation(XML架构建议)。这些类使你可以从文件中读取架构,或以编程方式在内存中创建可以编译和验证或可以写入到文件中的架构。
本文我们将以文档对象模型(DOM)为基础展开介绍。
二、XML的命名空间和相关类概览
.Net框架为我们提供了以下一些命名空间:System.Xml、System.Xml.Schema、System.Xml.Serialization、System.Xml.Xpath以及 System.Xml.Xsl来包容和XML操作相关的类。本文中我们将主要讨论System.Xml命名空间,其他的相关内容请读者自行查阅相关资料。
System.Xml命名空间包含各种各样的XML类,这些类可使用读取器、编写器和符合WWW 联合会 (W3C) DOM 要求的组件来对 XML 数据进行分析、验证和操作。以下列表包含XML命名空间中主要的类:
XmlNode 是重要的抽象类,DOM树中的每个节点都应是它的派出。
XmlDocument类 实现 W3C 文档对象模型级别1核心以及核心DOM级别2。
XmlTextReader类 提供对XML数据的快速、非缓存和只进的读取访问。
XmlNodeReader类 为给定的DOM节点子树提供 XmlReader。
XmlValidatingReader类 提供DTD、XDR和XSD架构验证。
XmlTextWriter类 提供生成XML的快速、只进的方式。
XmlDataDocument类 提供可与数据集关联的XmlDocument的实现。可通过数据集的关系表示形式或XmlDataDocument的树表示形式同时查看和操作结构化XML。
XPathDocument类 为XSLT提供了一种进行 XML 文档处理的快速和高性能的缓存。
XPathNavigator类 为用于存储的W3C XPath 1.0数据模型提供了一种光标样式模型以便于浏览。
XslTransform类是与W3C XSLT 1.0规范兼容的XSLT处理器,用于转换XML文档。
XmlSchema 对象模型类提供一组可进行浏览的类,这些类直接反映W3C XSD规范。它们提供以编程方式创建XSD架构的功能。
XmlSchemaCollection类 提供XDR和XSD架构库。这些缓存在内存中的架构为XmlValidatingReader提供快速的、分析时验证XmlReader类。
三、XML文档对象模型(DOM)
.NET仅仅支持XML DOM模式,而不支持SAX模式。文档对象模型(DOM)类是XML文档的内存中表示形式,XML数据在内存中的表示是结构化的,一般使用树结构表示DOM对象模型。为了对DOM内存结构有更直观的认识,先看下例XML数据在内存的构造。
<xml version="1.0">
<book>
<author>江新</author>
<price>40</price>
</book>
1.读取XML数据
XML DOM模式提供多种方式读取XML数据,如从流、URL、文本读取器或XmlReader中读取。Load方法将文档读入内存中,并包含可用于从每个不同的的格式中获取数据的重载方法。如下段代码演示了多XmlReader读取器中读取XML数据。
// Create the validating reader and specify DTD validation.
txtReader = new XmlTextReader(filename);
reader = new XmlValidatingReader(txtReader);
reader.ValidationType = ValidationType.DTD;
// Set a handler to handle validation errors.
reader.ValidationEventHandler += eventHandler;
// Pass the validating reader to the XML document.
// Validation fails due to an undefined attribute, but the
// data is still loaded into the document.
XmlDocument doc = new XmlDocument();
doc.Load(reader);
Console.WriteLine(doc.OuterXml);
另外,还有一个简便的LoadXML方法,它直接从字符串中读取XML,如:
//新建XmlDocument对象。
XmlDocument doc = new XmlDocument();
//读取XML数据。
doc.LoadXml("<book><author>碧清</author><price>40</priec></book>");
2.访问DOM中的属性
XML文档中另一重要特性是属性,它表示元素的某种特性。下面我们学习如何访问到元素的属性。在DOM模式中,如当前节点是元素时,可先使用HasAttribute方法检测是否存在属性,如存在,可使用XmlElement.Attributes属性获取包含该元素所有属性的集合。一般情况下,可按如下步骤访问元素的属性:
//获取book节点元素的属性集合。
XmlAttributeCollection attrs = myxml.DocumentElement.
SelectSingleNode(“//book”).Attributes;
//从属性集合中提取属性ID。
XmlAttribute attr = attrs["ID"];
//获取属性ID的值。
string id = atrr.Value;
3.在DOM中创建新节点
可以通过在DOM树中插入新的节点来为XML文档新添新的元素。XmlDoument类中提供了创建所有类型节点的方法,如CreateElement、CreateTextNode等。创建了新节点后,可使用几种方法将其插入到DOM树中。
如下段代码新建一个元素节点:
//新建一个Element节点,将插入到book节点的子节点中。
XmlElement elem = doc.CreateElement("ISDN");
//新建一个Text节点,将其作为ISDN的值。
XmlText text = doc.CreateTextNode("CN94-0000/TP");
插入节点到DOM树中的方法共有如下几种:
InsertBefore:表示插入到引用节点之前。如,在位置5插入新节点:
XmlNode refChild=node.ChildNodes[4];
Node.InsertBefore(newChild,refChild);
InsertAfter:表示插入引用节点之后。
AppendChild:表示将节点添加到给定节点的子节点列表的未尾。
PrependChild:表示将节点添加到给定节点的子节点列表的开头。
Append:表示将XmlAttribute节点追加到与元素关联的属性集合的未尾。
下段代码将上面创建两个节点插入到book节点的子节点中:
//将elem节点插入到book节点的最后一个子节点处。
doc.DocumentElement.AppendChild(elem);
//将text节点添加作为elem节点的值。
doc.DocumentElement.LastChild.AppendChild(text);
4.在DOM中删除节点
在DOM中删除节点,可使用RemoveChild方法,如从DOM中删除多个节点可直接调用RemoveAll方法,它将删除当前节点的所有子级和属性。
如:
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" +
"<title>Pride And Prejudice</title>" +
"</book>");
XmlNode root = doc.DocumentElement;
//删除title元素节点。
root.RemoveChild(root.FirstChild);
如果仅仅删除元素节点的属性,可使用以下三种方法:
XmlAttributeCollection.Remove:删除特定属性。
XmlAttributeCollection.RemoveAll:删除集合中的所有属性。
XmlAttributeCollection.RemoveAt:通过索引号来删除集合中某属性。
如:
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" +
"<title>Pride And Prejudice</title>" +
"</book>");
XmlAttributeCollection attrColl = doc.DocumentElement.Attributes;
//删除属性genre。
attrColl.Remove(attrColl["genre"]);
5.保存XML文档
当应用程序对XML文档进行了有效的修改后,需要将内存中的内容保存到磁盘的XML文件中。保存XML文档很简单,调用Save方法即可。
下段程序代码演示了Save方法的使用:
using System;
using System.Xml;
public class Sample {
public static void Main() {
// Create the XmlDocument.
XmlDocument doc = new XmlDocument();
doc.LoadXml("<item><name>wrench</name></item>");
// Add a price element.
XmlElement newElem = doc.CreateElement("price");
newElem.InnerText = "10.95";
doc.DocumentElement.AppendChild(newElem);
// Save the document to a file and auto-indent the output.
XmlTextWriter writer = new XmlTextWriter("data.xml",null);
writer.Formatting = Formatting.Indented;
doc.Save(writer);
}
}
四、使用XmlReader阅读器访问XML文档
XmlReader是一个抽象类,它提供非缓存的、只进只读访问XML文档的功能。XmlReader立刻器工作原理类似于我们的桌面应用程序从数据库中取出数据的原理。数据库服务返回一个游标对象,它包含所有查询结果集,并返回指向目标数据集的开始地址的引用。XmlReader阅读器的客户端收到一个指向阅读器实例的引用。该实例提取底层的数据流并把取出的数据呈现为一棵XML树。阅读器类提供只读、向前的游标,你可以用阅读器类提供的方法滚动游标遍历结果集中的每一条数据。
作为阅读器的抽象基类,它让开发人员能够自定义自己类型的阅读器,在.NET类库中,已为应用程序实现了三种类型的阅读器类,即XmlTextReader、XmlValidatingReader或者 XmlNodeReader类。
阅读器的主要功能是读XML文档的方法和属性。其类中的Read方法是一个基本的读XML文档的方法,它以流形式读取XML文档中的节点(Node)。另外,类还提供了ReadString、ReadInnerXml、ReadOuterXml和ReadStartElement等更高级的读方法。除了提供读XML文档的方法外,XmlReader类还提供了MoveToAttribute、MoveToFirstAttribute、MoveToContent、MoveToFirstContent、MoveToElement以及 MoveToNextAttribute等具有导航功能的方法。
(一) 使用XmlReader阅读器
要想使用XMLReader阅读器,首先必须创建一个XmlReader派出类的实例对象,如:
XmlTextReader reader = new XmlTextReader(file)
使用一个文件流创建XmlTextReader阅读器。创建完一个阅读器对象后,可以作为XmlDocument.Load方法的参数使用(其访问XML文档方法在前面已介绍过);也可以直接使用阅读器读取文档结构内容,如下段代码演示了如何使用XmlTextReader阅读器读取XML文档:
// 创建一个XmlTextReader类使它指向目标XML文档
XmlTextReader reader = new XmlTextReader(file);
// 循环取出节点的文本并放入到StringWriter对象实例中
StringWriter writer = new StringWriter();
string tabPrefix = "";
while (reader.Read())
{
//输出开始标志,如果节点类型为元素
if (reader.NodeType == XmlNodeType.Element)
{
//根据元素所处节点的深度,加入reader.Depth个tab符,然后把元素名输出到<>中。
tabPrefix = new string('\t', reader.Depth);
writer.WriteLine("{0}<{1}>", tabPrefix, reader.Name);
}
else
{
//输出结束标志,如果节点类型为元素
if (reader.NodeType == XmlNodeType.EndElement)
{
tabPrefix = new string('\t', reader.Depth);
writer.WriteLine("{0}</{1}>", tabPrefix, reader.Name);
}
(二) XmlReader的属性
XmlReader类具有一些可以在读取时修改的属性,以及一些在读取开始后被更改时并不会使新设置影响读取的其他属性。下面我们对XmlReader属性做一下简要介绍。
AttributeCount属性:当在派生类中被重写时,获取当前节点上的属性数。
BaseURI属性:当在派生类中被重写时,获取当前节点的基 URI。
CanResolveEntity属性:获取一个值,该值指示此读取器是否可以分析和解析实体。
Depth属性:当在派生类中被重写时,获取 XML 文档中当前节点的深度。
EOF属性:当在派生类中被重写时,获取一个值,该值指示此读取器是否定位在流的结尾。
HasAttributes属性:获取一个值,该值指示当前节点是否有任何属性。
HasValue属性:当在派生类中被重写时,获取一个值,该值指示当前节点是否可以具有Value。
IsDefault属性:当在派生类中被重写时,获取一个值,该值指示当前节点是否是从 DTD 或架构中定义的默认值生成的属性。
IsEmptyElement属性:当在派生类中被重写时,获取一个值,该值指示当前节点是否是一个空元素(例如 )。
Item属性:已重载。当在派生类中被重写时,获取此属性的值。 在 C# 中,该属性为 XmlReader 类的索引器。
(三)XmlReader常用方法
1.ReadInnerXml和ReadOuterXml方法
XmlReader提供了ReadInnerXml和ReadOuterXml方法读取元素和属性内容。ReadInnerXml将所有内容(包括标记)当做字符串读取,而ReadOuterXml读取表示该节点和所有它的子级的内容(包括标记)。
假设XML文档:
<node>
this <child id="123"/>
</node>
ReadInnerXml调用将返回this <child id="123"/>,而ReadOuterXml调用将返回<node> this <child id="123"/></node>。
2.Read方法
表示从流中读取下一个节点。通常用在循环中。如:
XmlTextReader rdr=new XmlTextReader("book.xml");
while(rdr.Read())
{
….//依次读取各节点。
}
3.ReadAttributeValue方法
将属性值解析为一个或多个 Text、EntityReference 或 EndEntity 节点。如:
//Create the reader.
reader = new XmlTextReader(xmlFrag, XmlNodeType.Element, context);
//Read the misc attribute. The attribute is parsed
//into multiple text and entity reference nodes.
reader.MoveToContent();
reader.MoveToAttribute("misc");
while (reader.ReadAttributeValue()){
if (reader.NodeType==XmlNodeType.EntityReference)
Console.WriteLine("{0} {1}", reader.NodeType, reader.Name);
else
Console.WriteLine("{0} {1}", reader.NodeType, reader.Value);
}
4.ReadString方法
将元素或文本节点的内容当做字符串读取。如:
//Load the reader with the XML file.
reader = new XmlTextReader("elems.xml");
//Parse the XML and display the text content of each of the elements.
while (reader.Read()){
if (reader.IsStartElement()){
if (reader.IsEmptyElement)
Console.WriteLine("<{0}/>", reader.Name);
else{
Console.Write("<{0}> ", reader.Name);
reader.Read(); //Read the start tag.
if (reader.IsStartElement()) //Handle nested elements.
Console.Write("\r\n<{0}>", reader.Name);
Console.WriteLine(reader.ReadString()); //Read the text content of the element.
}
}
5.其他方法
MoveToElement:移动到包含当前属性节点的元素。
MoveToFirstAttribute:移动到第一个属性。
MoveToNextAttribute:移动到下一个属性。
Skip:跳过当前节点的子级。
五、使用XmlWriter类写XML文档
XmlWriter 是定义用于编写 XML 的接口的抽象基类。XmlWriter 提供只进、只读、不缓存的 XML 流生成方法。更重要的是,XmlWriter在设计时就保证所有的XML数据都符合W3C XML 1.0推荐规范,你甚至不用担心忘记写闭标签,因为XmlWriter搞定一切。
像XmlReader抽象类一样,应用必须使用XmlWriter的一个派出类来创建实例,用户可以自定义自己的XmlWriter派出类,.Net类库中提供XmlTextWriter派出类。
(一) 使用XmlWriter
使用XmlWrite之前,必须新建一个XmlWriter抽象类的派出类实例,再使用XmlWrite的WriteXXX方法写入元素、属性和内容,最后使用Close方法关闭XML文档。
下段代码演示使用XmlWriter编写XML文档的例子:
// 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();
(二) XmlWriter类的属性
XmlWriter类提供了如下属性:
WriteState属性:当在派生类中被重写时,获取编写器的状态。
XmlLang属性:当在派生类中被重写时,获取表示当前 xml:space 范围的 XmlSpace。
XmlSpace属性:当在派生类中被重写时,获取当前的 xml:lang 范围。
(三) XmlWriter类常用方法
1.WriteXXX方法
这是一系列写操作的方法,主要有:
WriteBase64方法:将指定的二进制字节编码为 Base64 并写出结果文本。
WriteBinHex方法:将指定的二进制字节编码为 BinHex 并写出结果文本。
WriteCData方法:写出包含指定文本的 <![CDATA[...]]> 块。
WriteCharEntity方法:为指定的 Unicode 字符值强制生成字符实体。
WriteChars方法:以每次一个缓冲区的方式写入文本。
WriteComment方法:写出包含指定文本的注释 <!--...-->。、
WriteDocType方法:写出具有指定名称和可选属性的 DOCTYPE 声明。
下段代码演示了WriteChars方法的用法,其他方法用法类似。
//Handling surrogate pair across buffer streams.
char [] charArray = new char[4];
char lowChar, highChar;
Random random = new Random();
lowChar = Convert.ToChar(random.Next(0xDC01, 0xDFFF));
highChar = Convert.ToChar(random.Next(0xD801, 0xDBFF));
XmlTextWriter tw = new XmlTextWriter("test.xml", null);
tw.WriteStartElement("Root");
charArray[0] = 'a';
charArray[1] = 'b';
charArray[2] = 'c';
charArray[3] = highChar;
try
{
tw. WriteChars(charArray, 0, charArray.Length);
}
catch (Exception ex) {
}
Array[0] = highChar;
Array[1] = lowChar;
charArray[2] = 'd';
tw.WriteChars(charArray, 0, 3);
tw.WriteEndElement();
2.Flush方法
该方法用于将缓冲区中的所有内容刷新到基础流,并同时刷新基础流。下段代码演示了Flush方法的使用。
XmlTextWriter writer = new XmlTextWriter (Console.Out);
//Use indenting for readability
writer.Formatting = Formatting.Indented;
//Write an XML fragment.
writer.WriteStartElement("book");
writer.WriteElementString("title", "Pride And Prejudice");
writer.WriteEndElement();
writer.Flush();
//Write another XML fragment.
writer.WriteStartElement("cd");
writer.WriteElementString("title", "Americana");
writer.WriteEndElement();
writer.Flush();
//Close the writer.
writer.Close();
3.Close方法
该方法当应用在XML文档的写操作完成后,关闭文档流和基础流。
六、一个完整的XML文件读/写实例
本文最后将使用VS.net开发工具附带的"books.xml"文件来作为示例,为你介绍C#对XML文件的基本操作方法。我们假定books.xml文件的路径为:C:\books.xml。
该文件信息如下:
"books.xml"文件如下:
<xml version='1.0'>
<!-- This file represents a fragment of a book store inventory database -->
<bookstore>
<book genre="autobiography" publicationdate="1981" ISBN="1-861003-11-0">
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Benjamin</first-name>
<last-name>Franklin</last-name>
</author>
<price>8.99</price>
</book>
<book genre="novel" publicationdate="1967" ISBN="0-201-63361-2">
<title>The Confidence Man</title>
<author>
<first-name>Herman</first-name>
<last-name>Melville</last-name>
</author>
<price>11.99</price>
</book>
<book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6">
<title>The Gorgias</title>
<author>
<first-name>Sidas</first-name>
<last-name>Plato</last-name>
</author>
<price>9.99</price>
</book>
</bookstore>
1.读取XML文件的方法
XmlReader类是提供XML分析的API,XmlTextReader是旨在处理字节流的实现。 通常情况下,如果你需要将XML作为原始数据进行访问,则可以使用XmlTextReader,这样可不必避免DOM开销。省去对DOM的访问可使读取XML的速度加快。例如,XML文档可能有一个标头节,用于传递该文档以便在其他地方进行处理。XmlTextReader有不同的构造函数,以指定XML数据的位置。
XmlTextReader reader = new XmlTextReader ("books.xml");
加载后,XmlTextReader 通过使用 Read 方法在 XML 数据中移动,按顺序从文档中检索下一个记录。如果不再有记录,则 Read 方法返回假。
while (reader.Read())
{
// Do some work here on the data
...
}
Do While (reader.Read())
' Do some work here on the data
...
Loop
要处理 XML 数据,每个记录都有一个可从 NodeType 属性确定的节点类型。NodeType 枚举返回节点类型后,该示例将测试此节点类型,以查看该类型是元素类型还是文档类型。如果该节点是两种类型中的一种,则此示例将使用 Name 和 Value 属性处理该节点,以显示有关该节点的详细信息。Name 属性返回节点名(如元素和属性名),而 Value 属性返回当前节点(记录)的节点值(节点文本)。
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: // The node is an Element
Console.Write("<" + reader.Name);
while (reader.MoveToNextAttribute()) // Read attributes
Console.Write(" " + reader.Name + "='" + reader.Value + "'");
Console.Write(">");
break;
case XmlNodeType.DocumentType: // The node is a DocumentType
Console.WriteLine(NodeType + "<" + reader.Name + ">" + reader.Value);
break;
...
}
}
Do While (reader.Read())
Select Case reader.NodeType
Case XmlNodeType.Element ' The node is an Element
Console.Write("<" + reader.Name)
while (reader.MoveToNextAttribute()) ' Read attributes
Console.Write(" " + reader.Name + "='" + reader.Value + "'")
end while
Console.Write(">")
Case XmlNodeType.DocumentType ' The node is a DocumentType
Console.WriteLine(NodeType & "<" & reader.Name & ">" & reader.Value);
...
End Select
Loop
2.写XML文件的方法
首先,我们要创建一个XmlTextWriter类的实例对象。该类的构造函数XmlTextWriter有三种重载形式,其参数分别为一个字符串、一个流对象和一个TextWriter对象。这里我们运用字符串的参数形式,该字符串就指明了所要创建的XML文件的位置,方法如下:
XmlTextWriter textWriter = New XmlTextWriter("C:\\books.xml", null);
在创建完对象后,我们调用WriterStartDocument方法开始写XML文档,下面的实例就是介绍如何具体运用这些方法来完成XML文档的写工作的。
using System;
using System.Xml;
namespace WriteXML
{
class Class1
{
static void Main( string[] args )
{
// 创建XmlTextWriter类的实例对象
XmlTextWriter textWriter = new XmlTextWriter("C:\\myXmFile.xml", null);
// 开始写过程,调用WriteStartDocument方法
textWriter.WriteStartDocument();
// 写入说明
textWriter.WriteComment("First Comment XmlTextWriter Sample Example");
textWriter.WriteComment("myXmlFile.xml in root dir");
// 写入一个元素
textWriter.WriteStartElement("Name", "");
textWriter.WriteString("Student");
textWriter.WriteEndElement();
// 再写入一个元素
textWriter.WriteStartElement("Address", "");
textWriter.WriteString("Colony");
textWriter.WriteEndElement();
// 写入字符
char [] ch = new char[3];
ch[0] = 'a';
ch[1] = 'r';
ch[2] = 'c';
textWriter.WriteStartElement("Char");
textWriter.WriteChars(ch, 0, ch.Length);
textWriter.WriteEndElement();
// 写文档结束,调用WriteEndDocument方法
textWriter.WriteEndDocument();
// 关闭textWriter
textWriter.Close();
}
}
}
七、总结
本文为大家介绍了C#编写XML文件的相关知识,同时也对.Net中提供的XML命名空间、XML文档对象模型(DOM)以及相关类进行了介绍。通过本文的介绍,相信读者朋友已经这两方面的有了直观的认识。这也是本文的主要目的所在。