最近在用XML做游戏关卡存档使用了XML,之前有过一次整理,但是发现整理的有点垃圾,索性这次一次将这一块的知识点重新整理一遍,方便快速入门
简介
XML文件说明
<!-- -->
<!-- 单行注释 -->
<!--
xml对大小写敏感
名称不能以数字或标点开始
也不能以xml、Xml、XML开始
不能有空格
-->
<!-- 必须要写节点声明,一般值为这个 -->
<?xml version="1.0" encoding="utf-8" ?>
<!--xml文件必须有一个根节点,且只有一个根-->
<root>
<subchild data = "2022">
元素
</subchild>
</root>
<!--标签的属性必须加"",属性值是某个标签的附加属性-->
<root data = "2022年"> </root>
<!-- 单标签(单节点)下面两个是相同的 -->
<root />
<root></root>
<!--可以存在同名标签-->
<root>
<child></child>
<child></child>
</root>
xml的实体引用(特殊转义)
实体引用[还要加个;] | 代表 |
---|---|
<; | < |
>; | > |
&; | & |
&apos; | ’ |
"; | " |
<!-- 错误写法 -->
<test>1 < 2 </test>
<!-- 正确写法 -->
<test>1 < 2 </test>
XML操作
常见节点说明
常见节点有:XmlNode,XmlAttribute,XmlElement,XmlDocument,XmlDeclaration;所有的节点都是从XmlNode继承下来;XmlElement和XmlDeclaration有些相似是继承XmlLinkedNode,XmlLinkedNode是继承XmlNode,看名字就知道类似模拟链表的操作(像但不是)
XmlNode
在Xml文档中表示单个节点
属性
- Attributes 当前节点的所有属性(可以通过名称,或者所属下标位置来访问)
- ChildNodes 返回所有的子节点
- FirstChild 第一个子节点
- HasChildNodes 是否有子节点
- LastChild 最后一个子节点
- InnerText 获取或设置当前节点的内容(元素);元素以字符串形式传入,会转义
- InnerXml 获取或设置当前节点的内容(元素);元素以xml形式传入
- this.[string] 查找第一个名称符合的子节点
- Name 返回当前节点的限定名称(命名空间+LocalName)(如果没有命名空间则与LocalName一样)
- LocalName 返回当前节点的本地名称
- NextSibling 返回同级下一个兄弟节点
- OutXml 获取包含当前节点的内容以Xml形式返回
- OwnerDocument 获取所属的Document
- ParentNode 获取当前节点的父节点
- PreviousSibling 返回同级的上一个兄弟节点
- Value 返回当前节点的值;和InnerText很像但是对于不同节点类型有不同的返回结果;
- Element 返回null,使用InnerText代替
- Attribute 返回属性的内容
- NodeType 返回当前节点的类型
- Prefix 返回或设置此节点的命名空间前缀
方法
- AppendChild(XmlNode) 将指定节点添加到此节点的子节点末尾
- Clone() 创建此节点的副本,包括子树相当于调用CloneNode(true)
- CloneNode(bool) 创建此节点的副本,参数为true时克隆此节点下的子树,false时仅克隆节点本身
- InsertAfter(XmlNode A,XmlNode B) 将A节点插入到B节点后面;不允许B是A的子节点,A,B是不同文档(Document)创建的;
- InsertBefore(XmlNode A,XmlNode B) 将A节点插入到B节点前面面;不允许B是A的子节点,A,B是不同文档(Document)创建的;
- PrependChild(XmlNode) 将指定节点添加的此节的子节点表头
- RemoveAll() 移除当前节点的所有子节点和属性
- RemoveChild(XmlNode) 删除指定的子节点
- ReplaceChild(XmlNode new,XmlNode old) 将old替换成new【tip:old必须是该节点的子节点,不能是孙节点或更深】
- SelectNodes(String) 查找与XPath表达式匹配的节点列表。
- SelectSingleNode(String) 查找与XPath表达式匹配的第一个节点
- WriteContentTo(XmlWriter) 将节点的所有子节点保存到指定的XmlWriter(内容等效 InnerXml)
- WriteTo(XmlWriter) 将当前节点保存到指定的(内容等效 OuterXml)
XmlDocument
表示 XML 文档。 可使用此类在文档中加载、验证、编辑、添加和放置 XML。
属性
- DocumentElement 获取Xml文档树的根,不存在返回null(与FirstChild不同,FirstChild会获取到Xml的DOCTYPE声明,但是可以用LastChild代替获取根,因为根只能有一个)
- DocumentType 获取包含 DOCTYPE 声明的节点
- PreserveWhitespace 获取或设置一个值,该值指示是否在元素内容中保留空白区域。
- 在Load前为true,则保留文本的格式;为false时会则保留有效的空白(父子节点之间是两个空格的缩进)
- 在Save前为true,则文档中的空白将保留在输出中;为false时会自动缩进输出
方法
- CreateAttribute(String) 创建具有指定Name的XmlAtrribute
- CreateElement(String) 创建具有指定Name的XmlElement
- CreateXmlDeclaration(String version , String encoding, String standalone) 创建XmlDeclaration,version必须是“1.0”,encoding建议是“UTF-8”,standalone建议填“”或者String.Empty
- GetElementById(String) 获取具有指定 ID 的XmlElement(操作有点麻烦,不考虑)
- GetElementsByTagName(String) 返回一个XmlNodeList,包含所有节点名称与Name匹配的所有子元素;
- Load(String) 从指定的 URL 加载 XML 文档;还有很多加载的,略
- LoadXml(String) 从指定的字符串加载 XML 文档
- Save(String) 将 XML 文档保存到指定的文件。 如果存在指定文件,则此方法会覆盖它;还有很多保存的,略
XmlDeclaration
表示 XML 声明节点 <?xml version='1.0'...?>。
大部分都有,略
XmlElement
表示元素
属性
- HasAttributes 是否有任何属性
- IsEmpty 获取或设置元素的标记格式;为true时,格式为 ;为false时,或者有内容在里面;主动设置ture时会以短标记
方法
- GetAttribute(string) 返回具有指定名称的属性的值
- GetAttributeNode(String) 返回具有指定名称的XmlAttribute
- GetElementsByTagName(String) 返回一个XmlNodeList,包含所有节点名称与Name匹配的所有子元素;
- HasAttribute(String) 确定当前节点是否具有带有指定名称的属性
- RemoveAll() 移除当前节点的所有子节点和属性
- RemoveAllAttributes() 从元素中删除所有指定的属性(只针对当前节点,子节点不受影响)
- RemoveAttribute(String) 按名称删除指定属性
- RemoveAttributeAt(int) 按索引删除指定属性
- RemoveAttributeNode(XmlAttribute) 通过XmlAttribute来删除指定属性
- SetAttribute(String name, String value) 设置具有指定名称的属性的值
- SetAttributeNode(XmlAttribute) 添加指定的XmlAttribute
XmlAttribute
表示属性
属性
- Prefix 返回或设置此节点的命名空间前缀
- Value 返回或设置属性的值
方法
基本上用不到方法,略
XML输入输出流
XmlReader
表示提供对 XML 数据进行快速、非缓存、只进访问的读取器。
XmlReader reader = XmlReader.Create(string path); // 创建读取器实例
reader.Read(); reader.ReadAsync(); // 读取器读取下一个节点(同步,异步);当读取不到时返回false
reader.NodeType; // 当前节点类型
reader.Name; // 当前节点名称
reader.Value; // 当前节点内容
reader.Close(); // 关闭实例
<book>123</book>
对于一个节点分为:
XmlNodeType.Element(<book>)
XmlNodeType.Text(123)
XmlNodeType.EndElement(</book>)
所以reader.Read();时候是一个一个读
XmlWriter
表示一个写入器,该写入器提供一种快速、非缓存和只进方式以生成包含 XML 数据的流或文件。
XmlDocument xmlDoc = new XmlDocument();
var root = xmlDoc.CreateElement("root");
xmlDoc.AppendChild(root);
for(int i = 1; i < 6; i++) {
var child = root.OwnerDocument.CreateElement("item");
root.AppendChild(child);
child.InnerText = i.ToString();
}
XmlWriterSettings settings = new XmlWriterSettings(); // 创建写入器的设置
settings.Indent = true;
settings.IndentChars = "\t";
settings.Encoding = System.Text.Encoding.UTF8;
using (XmlWriter writer = XmlWriter.Create(folderFullPath + "/Test2.xml", settings)) {
xmlDoc.Save(writer); // 将xmldoc保存到指定的写入器中
}
XmlWriterSetting
属性 | 内容 | 默认值 |
---|---|---|
Encoding | 使用的文本编码 | utf-8 |
Indent | 是否缩进元素 | flase(无缩进) |
IndentChars | 缩进时使用的字符串 | 两个空格 |
NewLineChars | 用于换行的字符串 | \r\n |
OmitXmlDeclaration | 隐藏XML声明(抬头有版本和文本编码的声明) | false(不隐藏) |
案例
// 自己生成
XmlDocument xmlDoc = new XmlDocument(); // 创建文档
xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null)); // 添加声明
XmlElement root = xmlDoc.CreateElement("root"); // 创建节点(根)
xmlDoc.AppendChild(root); // 添加到末尾
for (int i = 1; i <= 5; i++) {
XmlElement child = xmlDoc.CreateElement("Book");
root.AppendChild(child);
child.SetAttribute("id", i.ToString()); // 设置属性
child.InnerText = i.ToString(); // 设置内容
}
xmlDoc.Save(Console.Out); // 保存到输出窗口
// 自己加载
XmlDocument xmlDoc1 = new XmlDocument();
xmlDoc1.Load(folderFullPath + "/Test.xml"); // 指定文件加载
string xml = File.ReadAllText(folderFullPath + "/Test.xml");
XmlDocument xmlDoc2 = new XmlDocument();
xmlDoc2.LoadXml(xml); // xml字符串加载
XPath
XPath 使用路径表达式来选取 XML 文档中的节点或节点集
语法
例子
<?xml version="1.0" encoding="UTF-8"?>
<root>
<skill name="普通攻击">轻轻的一击</skill>
<skill name="重击">沉重的一击</skill>
<skill name="必杀技">致命一击,大量伤害</skill>
<child name = "child1">
<name>1</name>
</child>
<child>
<name>2</name>
<cchild name = "child2">
<name>3</name>
</cchild>
</child>
<book>
<name>4</name>
</book>
</root>
选取节点
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有(叫nodename)子节点 |
/ | 从根节点选取(取子节点) |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
路径表达 | 结果 |
---|---|
skill(从root出发) | 得到root所有叫skill的子节点 |
/root/child/name | 从文档出发找到所有name节点的绝对路径满足这个(结果1,2都有) 注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
child/name | 从当前节点开始,选取所有child节点中的所有name节点(相对路径) |
//name | 选取所有name节点,而不管它们在文档中的位置(与当前节点无关) |
root/child//name | 选取所有root子节点中的所有child节点下的所以name节点(不管在child之下的位置)(比上面的查找全部name,这个是查找child下的所有name) |
//@name | 选取名为 name 的所有属性 |
root/skill/@name | 查找到所有root/skill节点所有名为 name 的所有属性 |
谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点
表达式 | 结果 |
---|---|
root/skill[1] | 选取root子节点第一个skill(下标是从1开始) |
root/skill[last()] | 选取root子节点最后个skill |
root/skill[last()-1] | 选取root子节点倒数第二个skill |
root/skill[position()<=2] | 选取root子节点最前面的两个skill(判断符号 <,<=,=,>,>=,!= 除了等于比较特殊) |
//skill[@name] | 选取所有拥有名为 name 的属性的 skill元素。 |
root/skill[@name = ‘重击’] | 选取所有拥有名为 name属性为重击 的 skill元素。 |
root/child[name > 1] | 选取所有root节点中所有child节点,且name节点的值大于1(值为数值的时候才能比较) |
选取未知
通配符 | 描述 |
---|---|
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
表达式 | 结果 |
---|---|
/root/* | 选取root下所有子元素 |
//* | 选取当前节点下的所有节点(以递归的顺序将所有节点输出) |
//child[@*] | 选取所有带属性的child节点 |
选取若干
使用 | 来去若干
表达式 | 结果 |
---|---|
/root/skill | /root/child/name | 选取root/skill和root/child/name |
运算符
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 6 + 4 | 10 |
- | 减法 | 6 - 4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。 |
!= | 不等于 | price!=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。 |
< | 小于 | price<9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。 |
<= | 小于或等于 | price<=9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。 |
> | 大于 | price>9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |