movin2333

一.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;
    }
}

 

相关文章: