.NET读、写、查、删、改XML文件
一、 XML简介
XML即可扩展标记语言(EXtensible Markup Language),是一种标记语言,很类似HTML。XML的设计宗旨是传输数据,而非显示数据。XML标签没有被预定义,需要用户定义标签。XML被设计为具有自我描述性并符合W3C的推荐标准。
XML已经被广泛的应用于软件开发的很多方面,特别是Web开发。常用于简化数据的存储和共享。
二、 XML语法
1) 可以根据应用编写有实际意义的标签
比如要描述班级可以定义<Class>标签,要描述学生可以定义<Student>标签。
2) 所有XML标签必须有关闭标签,如:
<Class>高三一班</Class>
</Class>是关闭标签;再如:
<Student>高三一班</Student>
</Student>是关闭标签。
3) XML标签也是区分大小写的:
<Class>高三一班</class>
上面写法是错误的,因为<Class>和</class>是不同的标签,</class>是<class>的关闭标签,而不是<Class>的关闭标签。
3) XML标签是可以嵌套的,而且必须正确嵌套,所谓嵌套也就是在其内的意思,把握这一点就不难看出一下哪个是错误的了,正确的写法如下:
<Class>
<Student>
</Student>
</Class>
错误的写法:
<Class>
<Student>
</Class>
</Student>
在编写XML代码时,注意保持清晰地层次结构。
4) XML必须有根元素,根元素有且只能有一个
<Root>
<Child>1</Child>
<Child>2</Child>
<Child>3</Child>
</Root>
5) 可以给XML标签定义属性,属性值必须放在英文半角双引号里面。
<Class Name=”高三一班”>
<Student>
</Student>
</Class>
下面属性是错误的:
<Class Name=高三一班>
<Student>
</Student>
</Class>
6) 实体引用
如果你把字符 "<" 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。
这样会产生 XML 错误:
<message>if salary < 1000 then</message>
为了避免这个错误,请用一个实体引用来代替 "<" 字符:
<message>if salary < 1000 then</message>
在 XML 中,有 5 个预定义的实体引用:
< | < | 小于 |
> | > | 大于 |
& | & | 和号 |
' | ' | 单引号 |
" | " | 引号 |
7) XML中的注释
<!-- This is a comment -->
8) XML中的空格会被保留,不会被删减。
9) XML 命名规则
XML 元素必须遵循以下命名规则:
r 名称可以含字母、数字以及其他的字符
r 名称不能以数字或者标点符号开始
r 名称不能以字符 “xml”(或者 XML、Xml)开始
r 名称不能包含空格
r 可使用任何名称,没有保留的字词。
三、 XML验证
XML文档需要遵循下面几点:
r XML 文档必须有根元素
r XML 文档必须有关闭标签
r XML 标签对大小写敏感
r XML 元素必须被正确的嵌套
r XML 属性必须加引号
为了确保XML文档是“合法”,用DTD(文档类型定义)进行了验证。DTD 的作用是定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构:
<!DOCTYPE Student [
<!ELEMENT Student (Name,Age)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Age (#PCDATA)>
]>
验证时的使用方法:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Stude SYSTEM "Student.dtd">
<Student>
<Name>Tom</Name>
<Age>25</Age>
</Student>
也可以将DTD生命于XML文档中:
<?xml version="1.0"?>
<!DOCTYPE Student [
<!ELEMENT Student (Name,Age)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Age (#PCDATA)>
]>
<Student>
<Name>Tom</Name>
<Age>25</Age>
</Student>
关于以上 DTD 解释如下:
!DOCTYPE Student (第二行)定义此文档是 Student 类型的文档。
!ELEMENT Student (第三行)定义 Student 元素有二个元素:"Name,Age"
!ELEMENT Name (第四行)定义 Name 元素为 "#PCDATA" 类型
!ELEMENT Age (第五行)定义 Age 元素为 "#PCDATA" 类型
PCDATA
PCDATA 的意思是被解析的字符数据(parsed character data)。
可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。
PDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。
文本中的标签会被当作标记来处理,而实体会被展开。
不过,被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们。
CDATA
CDATA 的意思是字符数据(character data)。
CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
XSLT 是首选的 XML 样式表语言。
XSLT (eXtensible Stylesheet Language Transformations) 远比 CSS 更加完善。
使用 XSLT 的方法之一是在浏览器显示 XML 文件之前,先把它转换为 HTML
本文对XSLT不算详细介绍,请读者查阅XSLT相关书籍资料。
四、 编写XML文档
.NET System.XML命名空间下提供的一些类库使得开发人员可以很方便的对XML进行读、写、查。
XmlTextWriter类允许你将XML写到一个文件中去。这个类包含了很多方法和属性,使用这些属性和方法可以使你更容易地处理XML。下表是XmlTextWriter的方法:
方法名 | 功能描述 |
WriteStartDocument | 版本为“1.0”的 XML 声明 |
WriteEndDocument | 关闭任何打开的元素或属性 |
Close | 关闭此流和基础流 |
WriteDocType | 写出具有指定名称和可选属性的 DOCTYPE 声明 |
WriteStartElement | 写出具有指定的本地名称的开始标记 |
WriteEndElement | 关闭任何打开的元素或属性 |
WriteFullEndElement | 关闭一个元素,并且总是写入完整的结束标记 |
WriteElementString | 写出包含字符串值的元素 |
WriteStartAttribute | 书写属性的起始内容 |
WriteEndAttribute | 关闭上一个 WriteStartAttribute 调用 |
WriteRaw | 手动书写原始标记 |
WriteString | 书写一个字符串 |
WriteAttributeString | 出具有指定值的属性 |
WriteCData | 写出包含指定文本的 <![CDATA[...]]> 块 |
WriteComment | 写出包含指定文本的注释 <!--...--> |
WriteWhiteSpace | 写出给定的空白 |
WriteProcessingInstruction | 写出在名称和文本之间带有空格的处理指令,如下所示:<?name text?> |
下例是用XmlTextWriter类写的一个简单XML文档:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace XML
{
class Program
{
static void Main(string[] args)
{
XmlTextWriter writer = new XmlTextWriter("demo.xml", Encoding.UTF8);
//设置自动换行缩进
writer.Formatting = Formatting.Indented;
//XML版本声明
writer.WriteStartDocument();
//根元素
writer.WriteStartElement("Student");
//扩展子元素,这里只写了2个
writer.WriteElementString("Name", "长江");
writer.WriteElementString("Name", "黄河");
//关闭根元素,并书写结束标签
writer.WriteEndElement();
//将XML写入文件并且关闭XmlTextWriter实例对象
writer.Close();
}
}
}
运行后,会在项目根目录/bin/debug文件夹下生成demo.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<Student>
<Name>长江</Name>
<Name>黄河</Name>
</Student>
下面例子,我们增加一些XmlTextWriter类中的方法的应用:
XmlTextWriter writer = new XmlTextWriter("book.xml", null);
writer.WriteStartDocument();
//设置自动缩进
writer.Formatting = Formatting.Indented;
//根元素
writer.WriteStartElement("Books");
//第一个元素
writer.WriteStartElement("Book");
//向先前创建的元素中添加一个属性
writer.WriteAttributeString("BookName", ".NET框架");
//添加子元素
writer.WriteElementString("Author", "Jim");
writer.WriteElementString("Price", "40.00");
//关闭item元素
writer.WriteEndElement(); // 关闭元素
//在节点间添加一些空格
writer.WriteWhitespace("\n");
//使用原始字符串书写第二个元素
writer.WriteRaw("<Book BookName=\"C#\">" +
"<Author>Tom</Author>" +
"<Price>60.00</Price>" +
"</Book>");
//使用格式化的字符串书写第三个元素
writer.WriteRaw("\n <Book BookName=\"ASP.NET\">\n" +
" <Author>David</Author>\n" +
" <Price>100.00</Price>\n" +
" </Book>\n");
// 关闭根元素
writer.WriteFullEndElement();
//将XML写入文件并关闭writer
writer.Close();
运行代码后生成的XML文件代码如下:
<?xml version="1.0"?>
<Books>
<Book BookName=".NET框架">
<Author>Jim</Author>
<Price>40.00</Price>
</Book>
<Book BookName="C#"><Author>Tom</Author><Price>60.00</Price></Book>
<Book BookName="ASP.NET">
<Author>David</Author>
<Price>100.00</Price>
</Book>
</Books>
五、 XML读、查、删
上面我们学习了用XmlTextWriter对象创建一个XML文档,在本节里,将继续介绍如何在已有XML文档中查询和插入节点。下面示例在book.xml根节点下加入新的<Book>节点:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("book.xml");
//查找<Books>
XmlNode root = xmlDoc.SelectSingleNode("Books");
//创建一个<Book>节点
XmlElement el = xmlDoc.CreateElement("Book");
//设置<Book>节点的属性BookName
el.SetAttribute("BookName", "Windows Application");
//创建<Book>节点的第一个下级节点
XmlElement elSubAuthor = xmlDoc.CreateElement("Author");
elSubAuthor.InnerText = "Jack";
el.AppendChild(elSubAuthor);
//创建<Book>节点的第二个下级节点
XmlElement elSubPrice = xmlDoc.CreateElement("Price");
elSubPrice.InnerText = "70.00";
el.AppendChild(elSubPrice);
//添加<Book>节点到<Books>中
root.AppendChild(el);
xmlDoc.Save("book.xml");
运行代码后,book.xml代码如下,加粗部分是新增的代码:
<?xml version="1.0"?>
<Books>
<Book BookName=".NET框架">
<Author>Jim</Author>
<Price>40.00</Price>
</Book>
<Book BookName="C#">
<Author>Tom</Author>
<Price>60.00</Price>
</Book>
<Book BookName="ASP.NET">
<Author>David</Author>
<Price>100.00</Price>
</Book>
<Book BookName="Windows Application">
<Author>Jack</Author>
<Price>70.00</Price>
</Book>
</Books>
下面示例演示了如何修改XML节点:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("book.xml");
XmlNodeList nodeList = xmlDoc.SelectSingleNode("Books").ChildNodes;
foreach (var node in nodeList)
{
XmlElement el = (XmlElement)node;
if (el.GetAttribute("BookName") == "C#")
{
el.SetAttribute("BookName", "精通C#");
}
}
xmlDoc.Save("book.xml");
如果要输出book.xml文档中所有节点的属性和值,可以尝试在上例foreach中实现。
运行后,book.xml的代码如下,原来的“C#”被更改为了“精通C#”。
<?xml version="1.0"?>
<Books>
<Book BookName=".NET框架">
<Author>Jim</Author>
<Price>40.00</Price>
</Book>
<Book BookName="精通C#">
<Author>Tom</Author>
<Price>60.00</Price>
</Book>
<Book BookName="ASP.NET">
<Author>David</Author>
<Price>100.00</Price>
</Book>
<Book BookName="Windows Application">
<Author>Jack</Author>
<Price>70.00</Price>
</Book>
</Books>
如果要删除节点属性请使用RemoveAttribute方法,如果要删除节点请使用RemoveChild方法,也可以使用RemoveAll方法删除所有节点。关于这类方法的使用不再赘述。
另外DataSet也可以读写XML文件:
//方法有重载
ds.ReadXml(Server.MapPath("txt.xml"));
//方法有重载
ds.WriteXml(Server.MapPath("txt.xml"));
doc.LoadXml(txtXMLEditor.Text);
doc.Save(Server.MapPath("XMLFile.xml"));
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace OperateXml
{
public class XmlHelper
{
/// <summary>
/// 创建XML文件
/// </summary>
/// <param name="fileName">XML文件路径和文件名</param>
/// <param name="rootElement">XML文件根元素名称</param>
public static void CreateXmlFile(string fileName,string rootElement)
{
if (!File.Exists(fileName))
{
//创建Xml文件
XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8);
//设置自动缩进
writer.Formatting = Formatting.Indented;
//XML版本声明
writer.WriteStartDocument();
//根元素
writer.WriteStartElement(rootElement);
//关闭根元素,并书写结束标签
writer.WriteEndElement();
//将XML写入文件并且关闭XmlTextWriter实例对象
writer.Close();
}
}
/// <summary>
/// 添加XML数据
/// </summary>
/// <param name="fileName">XML文件路径和文件名</param>
/// <param name="rootElement">XML文件根元素名称</param>
/// <param name="itemElement">XML文件二级元素名称</param>
/// <param name="childElement">XML文件三级元素名称和值</param>
public static void AppendChildElement(string fileName, string rootElement,string itemElement, Dictionary<string,string> childElement)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
XmlNode root = xmlDoc.SelectSingleNode(rootElement);
XmlElement el = xmlDoc.CreateElement(itemElement);
foreach(var item in childElement)
{
XmlElement elChild = xmlDoc.CreateElement(item.Key);
elChild.InnerText = item.Value;
el.AppendChild(elChild);
}
root.AppendChild(el);
xmlDoc.Save(fileName);
}
/// <summary>
/// 删除数据行
/// </summary>
/// <param name="fileName">XML文件</param>
/// <param name="rowNode">顶级节点</param>
/// <param name="elementID">要查找的三级节点的InnerText,根据此值删除二级节点(一条数据)</param>
public static void RemoveRecord(string fileName,string rowNode,string elementID)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
XmlNodeList nodeList = xmlDoc.SelectSingleNode(rowNode).ChildNodes;
foreach (XmlNode node in nodeList)
{
XmlElement el = (XmlElement)node;
XmlNodeList nodeListChild = el.ChildNodes;
foreach (XmlNode nodeChild in nodeListChild)
{
if (nodeChild.InnerText == elementID)
{
xmlDoc.SelectSingleNode(rowNode).RemoveChild(node);
break;
}
}
}
xmlDoc.Save((fileName));
}
}
}