Unity学习笔记--数据持久化XML文件(2)

发布时间 2023-12-01 16:56:48作者: 畅知

IXmlSerializable接口:

使用该接口可以帮助处理不能被序列化和反序列化的特殊类得到处理,

使特殊类继承IXmlSerializable接口 ,实现其中的读写方法,检测到读写方法被重写之后,

便会按照自定义实现的接口来实现方法。

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using UnityEngine;

namespace Building.XML
{
    /// <summary>
    /// 要存储的类 实现IXMLSerializer接口
    /// </summary>
    public class Test03:IXmlSerializable
    {
        public int testInt;
        public string testName;
        public int age;
        public XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// 在进行读操作 反序列化时候调用的方法
        /// </summary>
        /// <param name="reader"></param>
        public void ReadXml(XmlReader reader)
        {
            //读XML文件
            //001 读属性
            testInt = int.Parse(reader["testInt"]);
            testName = reader["testName"];
            //002 读节点
            //单个读
            reader.Read();//读节点名称
            reader.Read();//读节点中内容
            //获取内容数值
            testInt = int.Parse(reader.Value);
            reader.Read();//读结尾
            
            //读下一个开始
            reader.Read();
            reader.Read();
            testName = reader.Value;
            reader.Read();
            //或者循环读
            //=====进行读  对应这要读取的XML文件
            /*while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "testInt":
                            reader.Read();
                            testInt = int.Parse(reader.Value);
                            break;
                        case "testName":
                            reader.Read();
                            testName = reader.Value;
                            break;
                        case "testAge":
                            reader.Read();
                            age = int.Parse(reader.Value);
                            break;
                        
                    }
                }
            }
            */

            reader.Read();
            reader.Read();
            age = int.Parse(reader.Value);
            reader.Read();
            
            //003 读取包裹的元素节点
            XmlSerializer xmlSerializerInt = new XmlSerializer(typeof(int));
            XmlSerializer xmlSerializerString = new XmlSerializer(typeof(string));
            //跳过根节点--默认read停留在开始
            reader.Read();
            //读包裹开始
            reader.ReadStartElement("BlogName");
            //读包裹内容
            testName = xmlSerializerString.Deserialize(reader).ToString();
            reader.ReadEndElement();//读取包裹结束
            
            reader.ReadStartElement("Age");
            age = (int)xmlSerializerInt.Deserialize(reader);
            reader.ReadEndElement();
            
        }

        /// <summary>
        /// 在进行写操作 序列化时候调用的方法
        /// </summary>
        /// <param name="writer"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void WriteXml(XmlWriter writer)
        {
            // 向XML文件中写内容
            //1 写属性
            writer.WriteAttributeString("testInt",testInt.ToString());
            writer.WriteAttributeString("testName",testName);
            
            //2 写节点
            writer.WriteElementString("testInt",this.testInt.ToString());
            testName = "TonyChang";
            age = 21;
            writer.WriteElementString("testName",testName);
            writer.WriteElementString("testAge",age.ToString());
            //3.写包裹的节点
            XmlSerializer serializerInt = new XmlSerializer(typeof(int));
            XmlSerializer serializerString = new XmlSerializer(typeof(string));
            
            writer.WriteStartElement("BlogName");//开始包裹
            serializerString.Serialize(writer,testName);//包裹中内容 序列化放到写入流中
            writer.WriteEndElement();//结束包裹
            
            writer.WriteStartElement("Age");
            serializerInt.Serialize(writer,age);
            writer.WriteEndElement();//结束包裹
        }
    }
    public class XMLDemo2:MonoBehaviour
    {
        private void Start()
        {
            //采用默认方式(上文中提到的)进行序列化与反序列化
            string path = Application.persistentDataPath + "/XMLDemo02.xml";
            Test03 test03 = new Test03();
            print(path);//打印存储路径
            using (StreamWriter streamWriter=new StreamWriter(path))
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(Test03));
                xmlSerializer.Serialize(streamWriter,test03);
            }
            
            //反序列化
            if (File.Exists(path))
            {
                using (StreamReader streamReader=new StreamReader(path))
                {
                    XmlSerializer xmlSerializerRead = new XmlSerializer(typeof(Test03));
                    Test03 test03Read = xmlSerializerRead.Deserialize(streamReader) as Test03;
                    print(test03.testName);
                    print(test03Read.age);
                }
            }
        }
    }
}

image-20231201134726377

存储的XML的文件样式(注意对照XML存储样式来理解读取和存储的代码)

特别注意在代码中的读取包裹节点时候先要跳过“根节点”,code Line81;

自定义实现支持序列化与反序列化的字典

using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Building.XML.SerializerDic
{
    /// <summary>
    /// 支持序列化与反序列化的字典
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class SerializerDictionary<TKey,TValue>:Dictionary<TKey,TValue>,IXmlSerializable
    {
        public XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// 反序列化 读取XML文件调用
        /// </summary>
        /// <param name="reader"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void ReadXml(XmlReader reader)
        {
            //新建写入器 反序列化工具
            XmlSerializer xmlSerializerKey = new XmlSerializer(typeof(TKey));
            XmlSerializer xmlSerializerValue = new XmlSerializer(typeof(TValue));
            
            //跳过根节点
            reader.Read();
            
            while (reader.NodeType!=XmlNodeType.EndElement)
            {
                TKey tk = (TKey)xmlSerializerKey.Deserialize(reader);
                TValue tv = (TValue) xmlSerializerValue.Deserialize(reader);
                this.Add(tk,tv);//添加到容器中
            }
        }

        /// <summary>
        /// 序列化 写入XML文件时候调用
        /// </summary>
        /// <param name="writer"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void WriteXml(XmlWriter writer)
        {
           //新建写入器 序列化工具
           XmlSerializer xmlSerializerKey = new XmlSerializer(typeof(TKey));
           XmlSerializer xmlSerializerValue = new XmlSerializer(typeof(TValue));

           foreach (KeyValuePair<TKey,TValue> kv in this)
           {
               xmlSerializerKey.Serialize(writer,kv.Key);
               xmlSerializerValue.Serialize(writer,kv.Value);
           }
        }
    }
}

测试代码:

using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;

namespace Building.XML.SerializerDic
{
    public class Test4
    {
        public int num = 4;
        public SerializerDictionary<int, string> sDic;
    }
    public class XMLDemo3:MonoBehaviour
    {
        private void Start()
        {
            string path = Application.persistentDataPath + "/XMLDemo4.xml";
            print(path);
            Test4 test4 = new Test4();
            test4.sDic = new SerializerDictionary<int, string>() {{1, "Tony"}, {2, "Tom"}, {3, "Frank"}};
            using (StreamWriter streamWriter=new StreamWriter(path))
            {
                XmlSerializer xmlSerializerWrite = new XmlSerializer(typeof(Test4));
                xmlSerializerWrite.Serialize(streamWriter,test4);
            }
            
            //进行反序列化读取
            if (File.Exists(path))
            {
                using (StreamReader streamReader=new StreamReader(path))
                {
                    XmlSerializer xmlSerializerRead = new XmlSerializer(typeof(Test4));
                    Test4 test4Read=xmlSerializerRead.Deserialize(streamReader) as Test4;
                    foreach (KeyValuePair<int,string> pair in test4Read.sDic)
                    {
                       Debug.Log("key:"+pair.Key+",Value:"+pair.Value);
                    }
                }
            }
        }
    }
}

存储生成的XML文件内容:

image-20231201153332043

XML存储工具类:

using System;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;

namespace Building.XML.SerializerDic
{
    public class XMLSerializerManager
    {
        private static XMLSerializerManager instance=new XMLSerializerManager();
        public  static XMLSerializerManager Instance=>instance;

        private XMLSerializerManager()
        {
        }
        /// <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 streamWriter=new StreamWriter(path))
            {
                XmlSerializer xmlSerializerWriter = new XmlSerializer(data.GetType());
                xmlSerializerWriter.Serialize(streamWriter,data);
            }
        }
        public object LodaData(Type type,string FileName)
        {
            string path = Application.persistentDataPath + "/" + FileName + ".xml";
            if (!File.Exists(path))
            {
                path = Application.streamingAssetsPath + "/" + FileName + ".xml";
                if (!File.Exists(path))
                {
                    //如果在持续存储文件夹下未发现 并且在 流文件夹中仍旧未发现
                    //则利用反射返回一个该类型的实例 其中的数据为默认值
                    return Activator.CreateInstance(type);
                }
            }

            using (StreamReader streamReader=new StreamReader(path))
            {
                XmlSerializer xmlSerializerReader = new XmlSerializer(type);
                return xmlSerializerReader.Deserialize(streamReader);
            }
        }
    }
}