当前位置:   article > 正文

Unity XML3——XML序列化_unity link.xml es3

unity link.xml es3

一、XML 序列化

​ 序列化:把对象转化为可传输的字节序列过程称为序列化,就是把想要存储的内容转换为字节序列用于存储或传递

​ 反序列化:把字节序列还原为对象的过程称为反序列化,就是把存储或收到的字节序列信息解析读取出来使用

(一)XML 序列化

1.准备数据结构

  1. public class Lesson1Test
  2. {
  3. public int testPublic = 10;
  4. private int testPrivate = 11;
  5. protected int testProtected = 12;
  6. internal int testInternal = 13;
  7. public string testPUblicStr = "123";
  8. public int testPro { get; set; }
  9. public Lesson1Test2 testClass = new Lesson1Test2();
  10. public int[] arrayInt = new int[3] { 5, 6, 7 };
  11. public List<int> listInt = new List<int>() { 1, 2, 3, 4 };
  12. public List<Lesson1Test2> listItem = new List<Lesson1Test2>() { new Lesson1Test2(), new Lesson1Test2() };
  13. // 不支持字典
  14. // public Dictionary<int, string> testDic = new Dictionary<int, string>() { { 1, "123" } };
  15. }
  16. public class Lesson1Test2
  17. {
  18. public int test1 = 1;
  19. public float test2 = 1.1f;
  20. public bool test3 = true;
  21. }
  22. Lesson1Test lt = new Lesson1Test();

2.进行序列化

XmlSerializer:用于序列化对象为 xml 的关键类

StreamWriter:用于存储文件

using:用于方便流对象释放和销毁

  1. using System.Xml.Serialization;
  2. // 第一步:确定存储路径
  3. string path = Application.persistentDataPath + "/Lesson1Test.xml";
  4. // 第二步:结合 using知识点 和 StreamWriter这个流对象 来写入文件
  5. // 括号内的代码:写入一个文件流 如果有该文件 直接打开并修改 如果没有该文件 直接新建一个文件
  6. // using 的新用法 括号当中包裹的声明的对象 会在 大括号语句块结束后 自动释放掉
  7. // 当语句块结束 会自动帮助我们调用 对象的 Dispose这个方法 让其进行销毁
  8. // using一般都是配合 内存占用比较大 或者 有读写操作时 进行使用的
  9. using (StreamWriter stream = new StreamWriter(path)) {
  10. // 第三步:进行xml文件序列化
  11. XmlSerializer s = new XmlSerializer(typeof(Lesson1Test));
  12. // 这句代码的含义 就是通过序列化对象 对我们类对象进行翻译 将其翻译成我们的xml文件 写入到对应的文件中
  13. // 第一个参数:文件流对象
  14. // 第二个参数:想要备翻译 的对象
  15. // 注意:翻译机器的类型 一定要和传入的对象是一致的 不然会报错
  16. s.Serialize(stream, lt);
  17. }

3.运行测试

运行后可以看到如下的文件内容(在 path 文件夹中查看)

可以发现,只能保存 public 类型的数据

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Lesson1Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  3. <testPublic>10</testPublic>
  4. <testPUblicStr>123</testPUblicStr>
  5. <testClass>
  6. <test1>1</test1>
  7. <test2>1.1</test2>
  8. <test3>true</test3>
  9. </testClass>
  10. <arrayInt>
  11. <int>5</int>
  12. <int>6</int>
  13. <int>7</int>
  14. </arrayInt>
  15. <listInt>
  16. <int>1</int>
  17. <int>2</int>
  18. <int>3</int>
  19. <int>4</int>
  20. </listInt>
  21. <listItem>
  22. <Lesson1Test2>
  23. <test1>1</test1>
  24. <test2>1.1</test2>
  25. <test3>true</test3>
  26. </Lesson1Test2>
  27. <Lesson1Test2>
  28. <test1>1</test1>
  29. <test2>1.1</test2>
  30. <test3>true</test3>
  31. </Lesson1Test2>
  32. </listItem>
  33. <testPro>0</testPro>
  34. </Lesson1Test>

4.自定义节点名或设置属性

  1. public class Lesson1Test
  2. {
  3. [XmlElement("testPublic123123")] // 将该变量对应的结点名字改为 "testPublic123123"
  4. public int testPublic = 10;
  5. private int testPrivate = 11;
  6. protected int testProtected = 12;
  7. internal int testInternal = 13;
  8. public string testPUblicStr = "123";
  9. public int testPro { get; set; }
  10. public Lesson1Test2 testClass = new Lesson1Test2();
  11. public int[] arrayInt = new int[3] { 5, 6, 7 };
  12. [XmlArray("IntList")] // 改变数组对应的结点名字
  13. [XmlArrayItem("Int32")] // 改变数组成员对应的结点名字
  14. public List<int> listInt = new List<int>() { 1, 2, 3, 4 };
  15. public List<Lesson1Test2> listItem = new List<Lesson1Test2>() { new Lesson1Test2(), new Lesson1Test2() };
  16. // 不支持字典
  17. // public Dictionary<int, string> testDic = new Dictionary<int, string>() { { 1, "123" } };
  18. }
  19. public class Lesson1Test2
  20. {
  21. [XmlAttribute("Test1")] // 将该变量存储为XML属性,并改名为 "Test1"
  22. public int test1 = 1;
  23. [XmlAttribute] // 将该变量存储为XML属性
  24. public float test2 = 1.1f;
  25. [XmlAttribute]
  26. public bool test3 = true;
  27. }
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Lesson1Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  3. <testPublic>10</testPublic>
  4. <testPUblicStr>123</testPUblicStr>
  5. <testClass Test1="1" test2="1.1" test3="true" />
  6. <arrayInt>
  7. <int>5</int>
  8. <int>6</int>
  9. <int>7</int>
  10. </arrayInt>
  11. <IntList>
  12. <Int32>1</Int32>
  13. <Int32>2</Int32>
  14. <Int32>3</Int32>
  15. <Int32>4</Int32>
  16. </IntList>
  17. <listItem>
  18. <Lesson1Test2 Test1="1" test2="1.1" test3="true" />
  19. <Lesson1Test2 Test1="1" test2="1.1" test3="true" />
  20. </listItem>
  21. <testPro>0</testPro>
  22. </Lesson1Test>

​5. 总结:

  • 序列化流程
    1. 有一个想要保存的类对象
    2. 使用 XmlSerializer 序列化该对象
    3. 通过 StreamWriter 配合 using 将数据存储 写入文件
  • 注意:
    1. 只能序列化公共成员
    2. 不支持字典序列化
    3. 可以通过特性修改节点信息或者设置属性信息
    4. Stream 相关要配合 using 使用

二、XML 反序列化

(一)判断文件是否存在

  1. using System.IO;
  2. string path = Application.persistentDataPath + "/Lesson1Test.xml";
  3. if(File.Exists(path)) { ... }

(二)反序列化

​ 关键知识:

  1. using 和 StreamReader
  2. XmlSerializer 的 Deserialize 反序列化方法
  1. using System.Xml.Serialization;
  2. // 读取文件
  3. using (StreamReader reader = new StreamReader(path))
  4. {
  5. // 产生了一个 序列化反序列化的翻译机器
  6. XmlSerializer s = new XmlSerializer(typeof(Lesson1Test));
  7. Lesson1Test lt = s.Deserialize(reader) as Lesson1Test;
  8. }

​ 运行后调试,可以发现 List 类型的内容被重复添加,原因是变量 lt 初始化后, List 中有默认值,而反序列化时,Deserialize 方法会往 List 中用 Add 方法添加值,而不是覆盖原有的值。

​ 总结:

  1. 判断文件是否存在 File.Exists()

  2. 文件流获取 StreamReader reader = new StreamReader(path)

  3. 根据文件流 XmlSerializer 通过 Deserialize 反序列化出对象

​ 注意:List 对象如果有默认值,反序列化时不会清空,会往后面添加

三、IXmlSerializable 接口

​ C# 的 XmlSerializer 提供了可拓展内容,可以让一些不能被序列化和反序列化的特殊类能被处理
​ 让特殊类继承 IXmlSerializable 接口,实现其中的方法即可

(一)回顾序列化与反序列化

  1. using System.IO;
  2. using System.Xml;
  3. using System.Xml.Serialization;
  4. public class TestLesson3 : IXmlSerializable
  5. {
  6. public int test1;
  7. public string test2;
  8. }
  9. TestLesson3 t = new TestLesson3();
  10. t.test2 = "123";
  11. string path = Application.persistentDataPath + "/TestLesson3.xml";
  12. // 序列化
  13. using (StreamWriter writer = new StreamWriter(path))
  14. {
  15. // 序列化"翻译机器"
  16. XmlSerializer s = new XmlSerializer(typeof(TestLesson3));
  17. // 在序列化时 如果对象中的引用成员 为空 那么xml里面是看不到该字段的
  18. s.Serialize(writer, t);
  19. }
  20. // 反序列化
  21. using (StreamReader reader = new StreamReader(path))
  22. {
  23. // 序列化"翻译机器"
  24. XmlSerializer s = new XmlSerializer(typeof(TestLesson3));
  25. TestLesson3 t2 = s.Deserialize(reader) as TestLesson3;
  26. }
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TestLesson3 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  3. <test1>0</test1>
  4. <test2>123</test2>
  5. </TestLesson3>

(二)继承 IXmlSerializable 接口

1.继承接口并实现接口函数

  1. public class TestLesson3 : IXmlSerializable
  2. {
  3. public int test1;
  4. public string test2;
  5. // 返回结构,返回 null 即可,不用过多了解
  6. public XmlSchema GetSchema()
  7. {
  8. return null;
  9. }
  10. // 反序列化时 会自动调用的方法
  11. public void ReadXml(XmlReader reader) { }
  12. // 序列化时 会自动调用的方法
  13. public void WriteXml(XmlWriter writer) { }
  14. }

2.WriteXml

  1. public void WriteXml(XmlWriter writer)
  2. {
  3. // 在里面可以自定义序列化 的规则
  4. // 如果要自定义 序列化的规则 一定会用到 XmlWriter中的一些方法 来进行序列化
  5. // 1.写属性
  6. writer.WriteAttributeString("test1", this.test1.ToString());
  7. writer.WriteAttributeString("test2", this.test2);
  8. // 2.写节点
  9. writer.WriteElementString("test1", this.test1.ToString());
  10. writer.WriteElementString("test2", this.test2);
  11. // 3.写包裹节点
  12. XmlSerializer s = new XmlSerializer(typeof(int));
  13. writer.WriteStartElement("test1"); // 写 <test1>
  14. s.Serialize(writer, test1); // 用序列化翻译机器写 test1 的内容
  15. writer.WriteEndElement(); // 写 </test1>
  16. XmlSerializer s2 = new XmlSerializer(typeof(string));
  17. writer.WriteStartElement("test2"); // 写 <test2>
  18. s.Serialize(writer, test2); // 用序列化翻译机器写 test2 的内容
  19. writer.WriteEndElement(); // 写 </test2>
  20. }

3.ReadXml

  1. public void ReadXml(XmlReader reader)
  2. {
  3. // 在里面可以自定义反序列化 的规则
  4. // 1.读属性
  5. this.test1 = int.Parse(reader["test1"]);
  6. this.test2 = reader["test2"];
  7. // 2.读节点
  8. // 方式一
  9. reader.Read(); // 这时是读到的test1节点 <test1>
  10. reader.Read(); // 这时是读到的test1节点包裹的内容 0
  11. this.test1 = int.Parse(reader.Value); // 得到当前内容的值=
  12. reader.Read(); // 这时读到的是尾部包裹节点 </test1>
  13. reader.Read(); // 这时是读到的test2节点 <test2>
  14. reader.Read(); // 这时是读到的test2节点包裹的内容 123
  15. this.test2 = reader.Value;
  16. // 方式二
  17. while (reader.Read())
  18. {
  19. if (reader.NodeType == XmlNodeType.Element)
  20. {
  21. switch (reader.Name)
  22. {
  23. case "test1":
  24. reader.Read();
  25. this.test1 = int.Parse(reader.Value);
  26. break;
  27. case "test2":
  28. reader.Read();
  29. this.test2 = reader.Value;
  30. break;
  31. }
  32. }
  33. }
  34. // 3.读包裹元素节点
  35. XmlSerializer s = new XmlSerializer(typeof(int));
  36. XmlSerializer s2 = new XmlSerializer(typeof(string));
  37. reader.Read(); // 跳过根节点
  38. reader.ReadStartElement("test1"); // 读 <test1>
  39. test1 = (int)s.Deserialize(reader); // 用反序列化翻译机器读 test1 的内容
  40. reader.ReadEndElement(); // 读 </test1>
  41. reader.ReadStartElement("test2"); // 读 <test2>
  42. test2 = s2.Deserialize(reader).ToString(); // 用反序列化翻译机器读 test2 的内容
  43. reader.ReadEndElement(); // 读 </test2>
  44. }

四、Dictionary 支持序列化与反序列化

  1. 我们没办法修改 C# 自带的类

  2. 那我们可以重写一个类继承 Dictionary,然后让这个类继承序列化拓展接口 IXmlSerializable

  3. 实现里面的序列化和反序列化方法即可

  1. public class SerizlizedDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
  2. {
  3. public XmlSchema GetSchema() {
  4. return null;
  5. }
  6. // 自定义字典的 反序列化 规则
  7. public void ReadXml(XmlReader reader) {
  8. XmlSerializer keySer = new XmlSerializer(typeof(TKey));
  9. XmlSerializer valueSer = new XmlSerializer(typeof(TValue));
  10. // 要跳过根节点
  11. reader.Read();
  12. // 判断 当前不是元素节点 结束 就进行 反序列化
  13. while (reader.NodeType != XmlNodeType.EndElement) {
  14. // 反序列化键
  15. TKey key = (TKey)keySer.Deserialize(reader);
  16. // 反序列化值
  17. TValue value = (TValue)valueSer.Deserialize(reader);
  18. // 存储到字典中
  19. this.Add(key, value);
  20. }
  21. }
  22. // 自定义 字典的 序列化 规则
  23. public void WriteXml(XmlWriter writer) {
  24. XmlSerializer keySer = new XmlSerializer(typeof(TKey));
  25. XmlSerializer valueSer = new XmlSerializer(typeof(TValue));
  26. foreach (KeyValuePair<TKey, TValue> kv in this) {
  27. // 键值对 的序列化
  28. keySer.Serialize(writer, kv.Key);
  29. valueSer.Serialize(writer, kv.Value);
  30. }
  31. }
  32. }

(一)序列化测试

  1. public class TestLesson4
  2. {
  3. public int test1;
  4. public SerizlizerDictionary<int, string> dic;
  5. }
  6. public class Lesson4 : MonoBehaviour
  7. {
  8. // Start is called before the first frame update
  9. void Start() {
  10. TestLesson4 tl4 = new TestLesson4();
  11. tl4.dic = new SerizlizerDictionary<int, string>();
  12. tl4.dic.Add(1, "123");
  13. tl4.dic.Add(2, "234");
  14. tl4.dic.Add(3, "345");
  15. string path = Application.persistentDataPath + "/TestLesson4.xml";
  16. using (StreamWriter writer = new StreamWriter(path)) {
  17. XmlSerializer s = new XmlSerializer(typeof(TestLesson4));
  18. s.Serialize(writer, tl4);
  19. }
  20. }
  21. }
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TestLesson4 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  3. <test1>0</test1>
  4. <dic>
  5. <int>1</int>
  6. <string>123</string>
  7. <int>2</int>
  8. <string>234</string>
  9. <int>3</int>
  10. <string>345</string>
  11. </dic>
  12. </TestLesson4>

(二)反序列化测试

  1. void Start() {
  2. TestLesson4 tl4 = new TestLesson4();
  3. using (StreamReader reader = new StreamReader(path)) {
  4. XmlSerializer s = new XmlSerializer(typeof(TestLesson4));
  5. tl4 = s.Deserialize(reader) as TestLesson4;
  6. }
  7. }

五、自定义 XML 数据管理类

  1. using System;
  2. using System.IO;
  3. using System.Xml.Serialization;
  4. using UnityEngine;
  5. public class XmlDataMgr
  6. {
  7. // 单例模式
  8. public static XmlDataMgr Instance { get; } = new XmlDataMgr();
  9. // 防止外部实例化该管理类
  10. private XmlDataMgr() { }
  11. /// <summary>
  12. /// 保存数据到xml文件中
  13. /// </summary>
  14. /// <param name="data">数据对象</param>
  15. /// <param name="fileName">文件名</param>
  16. public void SaveData(object data, string fileName) {
  17. // 1.得到存储路径
  18. string path = Application.persistentDataPath + "/" + fileName + ".xml";
  19. // 2.存储文件
  20. using (StreamWriter writer = new StreamWriter(path)) {
  21. // 3.序列化
  22. XmlSerializer s = new XmlSerializer(data.GetType());
  23. s.Serialize(writer, data);
  24. }
  25. }
  26. /// <summary>
  27. /// 从xml文件中读取内容
  28. /// </summary>
  29. /// <param name="type">对象类型</param>
  30. /// <param name="fileName">文件名</param>
  31. /// <returns></returns>
  32. public object LoadData(Type type, string fileName) {
  33. // 1.首先要判断文件是否存在
  34. string path = Application.persistentDataPath + "/" + fileName + ".xml";
  35. if (!File.Exists(path)) {
  36. path = Application.streamingAssetsPath + "/" + fileName + ".xml";
  37. if (!File.Exists(path)) {
  38. // 如果根本不存在文件 两个路径都找过了
  39. // 那么直接new 一个对象 返回给外部 无非 里面都是默认值
  40. return Activator.CreateInstance(type);
  41. }
  42. }
  43. // 2.存在就读取
  44. using (StreamReader reader = new StreamReader(path)) {
  45. // 3.反序列化 取出数据
  46. XmlSerializer s = new XmlSerializer(type);
  47. return s.Deserialize(reader);
  48. }
  49. }
  50. }
  51. if (!File.Exists(path)) {
  52. path = Application.streamingAssetsPath + "/" + fileName + ".xml";
  53. if (!File.Exists(path)) {
  54. // 如果根本不存在文件 两个路径都找过了
  55. // 那么直接new 一个对象 返回给外部 无非 里面都是默认值
  56. return Activator.CreateInstance(type);
  57. }
  58. }
  59. // 2.存在就读取
  60. using (StreamReader reader = new StreamReader(path)) {
  61. // 3.反序列化 取出数据
  62. XmlSerializer s = new XmlSerializer(type);
  63. return s.Deserialize(reader);
  64. }
  65. }
  66. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/115356
推荐阅读
相关标签
  

闽ICP备14008679号