一.XML基础语法
1.XML结构:XML是一种树结构的文本
2.XML注释:格式:<!--在其中书写注释-->,在注释中可以单行注释也可以多行注释
3.固定内容:<?xml version="1.0" encoding="UTF-8"?>版本和编码信息
4.基本语法:<root>...</root>,一个节点的内容使用尖括号包裹节点名称开始,尖括号包裹斜杠和节点名称结束,子节点的内容被包裹在这两个尖括号中间。通过在最下层的子节点的首尾尖括号之间包裹数据的形式保存数据。这种节点的树形结构和对象这种引用类型中包含值类型(没有子节点)和其他引用类型(有子节点)的形式是一致的。
5.属性:和元素节点只是写法上的区别,相当于简化的节点写法
1)<Father name="爸爸" age="52">...</Father>
2)<Father name="爸爸" age="52"/>
二.Unity中XML的存放位置
1.Resources 只读文件,打包后找不到
2.Application.streamingAssetsPath 可读,PC端可写,能找到
3.Application.dataPath 打包后找不到
4.Application.persistentDataPath 可读可写能找到
三.Unity中读取xml文件信息
1.常见的读取方式
1)XmlDocument:把数据加载到内存中,方便读取
2)XmlTextReader:以流形式加载数据,内存占用更少,但是单向只读
3)Linq
2.使用XmlDocument读取和存储信息的一些方法
1)LoadXml成员方法:将字符串加载到xml中
2)Load成员方法:将路径对应的xml文件的内容加载到xml中
3)SelectSingleNode成员方法:得到单个节点,存储为XmlNode对象,XmlNode中同样有这个成员方法得到节点的单个子节点
4)节点对象.Attributes[属性名].Value得到属性值,或者节点对象.Attributes.GetNamedItem(属性名).Value可以达到同样的效果
5)SelectNodes成员方法:得到节点下的所有同名节点,存储为XmlNodeList对象
6)CreateXmlDeclaration成员方法:创建固定版本和编码信息
7)AppendChild:把节点对象添加到xml中
8)CreateElement:创建节点
9)InnerText属性:节点中存储的信息
10)SetAttribute成员方法:添加属性
11)Save成员方法:保存
3.使用XmlDocument读取和存储信息的一些重要类
1)XmlNode:读取出的单个节点信息类
2)XmlNodeList:读取出的多个同名节点信息类存储到一个list中
3)XmlElement:存储时的单个节点信息类
4)XmlDeclaration:存储时创建出的xml文件固定版本和编码等信息类
四.xml序列化和通用保存加载类
1.xml序列化
1)序列化:把对象转化为可传输的字节序列过程称为序列化
反序列化:将字节序列还原为对象的过程称为反序列化
2)using关键字:using关键字的格式和if类似,都是在后面跟上一个小括号和一个大括号。using关键字后面的小括号中的内容一般是声明一个对象,这个对象需要是继承IDispose接口的对象,在大括号中的代码块调用完毕后,系统会自动调用小括号中对象的Dispose方法将对象释放。一般用于内存占用较大或者读写时操作。
3)序列化类XmlSerializer和输出流StreamWriter:序列化类的对象相当于一个翻译机器,负责将对象进行序列化翻译,而输出流的对象相当于传送带,负责传输序列化前后的信息。因此,new一个XmlSerializer类对象时需要指定类型(翻译的类的类型),而new一个StreamWriter类对象时需要指定文件地址(传动带的终点),“输入”或者“输出”相当于是说的传送方向(传送带是单向的);使用Serialize方法进行序列化翻译时需要指定流对象(传送带)和要序列化的对象。
4)示例:
TestClass test = new TestClass(); string path = Application.persistentDataPath + "/TestClass.xml"; //尝试创建一个文件流,需要指定文件地址,会自动打开文件(如果有文件直接打开,如果没有文件自动新建一个文件再打开) //using括号中声明的对象会在大括号中语句块结束后自动释放 //using在语句块结束后自动调用对象的Dispose方法(继承IDispose接口),让对象自行销毁 //一般在内存占用较大或者有读写操作时使用 using(StreamWriter stream = new StreamWriter(path)) { //序列化对象相当于翻译机器,将对象翻译为序列化文件,需要指定机器能翻译的对象的类型;流相当于传送带,负责运输翻译的对象和结果 //创建序列化对象,需要指定这个序列化对象的类型 XmlSerializer xs = new XmlSerializer(typeof(TestClass)); //调用序列化对象的Serialize方法将对象test通过stream流序列化 xs.Serialize(stream,test); }
进行序列化的代码。
public class TestClass { public int testPublic = 1; private int testPrivate = 2; protected int testProtected = 3; internal int testInternal = 4; public string testStrPublic = "12"; public int testPro { get; set; } public TestClass2 testClass = new TestClass2(); } public class TestClass2 { public int test1 = 1; public float test2 = 2.2f; public bool test3 = false; }
被序列化的对象的模板类。
<?xml version="1.0" encoding="utf-8"?> <TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <testPublic>1</testPublic> <testStrPublic>12</testStrPublic> <testClass> <test1>1</test1> <test2>2.2</test2> <test3>false</test3> </testClass> <testPro>0</testPro> </TestClass>
序列化出来的xml文件内容,可以看到只有public的属性和变量被存储下来了。
注意:这种序列化方式支持数组、list等,但是不支持dictionary。
5)使用特性为序列化进行自定义
[XmlAttribute()]:在类中的属性或变量上加上这个特性,这个属性或变量在序列化时会存储为属性,可以在括号中指定自定义属性名称
[XmlElement()]:在类中的属性或变量上加上这个特性,这个属性或变量在序列化时存储为节点(默认也是存储为节点),可以在括号中指定自定义节点名称
[XmlArrayItem()]:在类中的数组属性(变量)前加上这个特性,在括号内传入自定义数组的元素存储为的节点名称
[XmlArray()]:在类中的list属性(变量)前加上这个特性,在括号内传入自定义的list的元素存储为的节点名称
2.xml反序列化
序列化和反序列化的方法是一一对应的,把序列化过程反过来就可以,示例:
string path = Application.persistentDataPath + "/TestClass.xml"; TestClass test1 = null; using (StreamReader stream = new StreamReader(path)) { XmlSerializer xs = new XmlSerializer(typeof(TestClass)); test1 = (TestClass)xs.Deserialize(stream); }
注意:在反序列化时,如果类中声明变量时list类型的变量有默认值,会调用add方法往list中添加值,也就是说反序列化后默认值和反序列化的值都会存储在list变量中,因此尽量不要在创建对象时为list赋初值。
3.IXmlSerializable接口
继承IXmlSerializable接口能够自定义类的序列化和反序列化。继承后需要重写方法:
/// <summary> /// 返回结构 /// </summary> /// <returns></returns> public XmlSchema GetSchema() { return null; } /// <summary> /// 反序列化时自动调用的方法 /// </summary> /// <param name="reader"></param> public void ReadXml(XmlReader reader) { //读属性 this.testStrPublic = reader["testStrPublic"]; //读节点 //方式一: //每次reader读取一块内容(一个尖括号的内容或者尖括号包裹的信息),先读取节点开始的尖括号,再读取节点中的内容,再read就继续读取节点结束的尖括号 reader.Read(); reader.Read(); this.testPublic = int.Parse(reader.Value); //方式二: //循环读取 while (reader.Read()) { //判断读取出的类型,是节点开始标签(XmlNodeType.Element)、节点中包裹的内容(XmlNodeType.Text)还是节点结束标签(XmlNodeType.EndElement),根据读取出的类型作处理 if (reader.NodeType == XmlNodeType.Text) { //Name属性是节点的名称,根据节点名称作响应的处理 switch (reader.Name) { case "testPublic": break; } } } //读包裹节点 XmlSerializer xs = new XmlSerializer(this.testStrPublic.GetType()); //跳过根节点 reader.Read(); reader.ReadStartElement("testStrPublic"); this.testStrPublic = (string)xs.Deserialize(reader); reader.ReadEndElement(); } /// <summary> /// 序列化时自动调用的方法 /// </summary> /// <param name="writer"></param> public void WriteXml(XmlWriter writer) { //写属性 writer.WriteAttributeString("testStrPublic", this.testStrPublic); //写节点 writer.WriteElementString("testStrPublic", this.testStrPublic); //写包裹节点 writer.WriteStartElement("testStrPublic"); XmlSerializer xs = new XmlSerializer(this.testStrPublic.GetType()); xs.Serialize(writer,this.testStrPublic); writer.WriteEndElement(); }
4.让Dictionary支持xml序列化和反序列化
只需要重写一个继承Dictionary的类,这个类再实现IXmlSerializable接口,重写方法即可。
public class SerializerDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable { public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { XmlSerializer keyXS = new XmlSerializer(typeof(TKey)); XmlSerializer valueXS = new XmlSerializer(typeof(TValue)); //跳过根节点 reader.Read(); //没有到根节点的结束尖括号,就可以一直读取 while(reader.NodeType != XmlNodeType.EndElement) { TKey key = (TKey)keyXS.Deserialize(reader); TValue value = (TValue)valueXS.Deserialize(reader); this.Add(key, value); } } public void WriteXml(XmlWriter writer) { XmlSerializer keyXS = new XmlSerializer(typeof(TKey)); XmlSerializer valueXS = new XmlSerializer(typeof(TValue)); foreach(KeyValuePair<TKey,TValue> pair in this) { keyXS.Serialize(writer, pair.Key); valueXS.Serialize(writer, pair.Value); } } }
5.xml管理类
public class XmlDataManager { //单例模式模块 private static XmlDataManager instance; public static XmlDataManager Instance { get { if (instance == null) instance = new XmlDataManager(); return instance; } } private XmlDataManager() { } /// <summary> /// 数据存储类 /// </summary> /// <param name="data">存储的数据对象</param> /// <param name="fileName">文件名</param> public void SaveData(object data,string fileName) { string path = Application.persistentDataPath + "/" + fileName + ".xml"; using(StreamWriter writer = new StreamWriter(path)) { XmlSerializer xs = new XmlSerializer(data.GetType()); xs.Serialize(writer, data); } } /// <summary> /// 加载对象 /// </summary> /// <param name="type">对象类型</param> /// <param name="fileName">文件名称</param> /// <returns></returns> public object LoadData(Type type,string fileName) { string path = Application.persistentDataPath + "/" + fileName + ".xml"; if (!File.Exists(path)) { path = Application.persistentDataPath + "/" + fileName + ".xml"; if (!File.Exists(path)) { return Activator.CreateInstance(type); } } object result = null; using(StreamReader reader = new StreamReader(path)) { XmlSerializer xs = new XmlSerializer(type); result = xs.Deserialize(reader); } return result; } }