一、XML 序列化
? 序列化:把對象轉化為可傳輸的字節(jié)序列過程稱為序列化,就是把想要存儲的內容轉換為字節(jié)序列用于存儲或傳遞
??反序列化:把字節(jié)序列還原為對象的過程稱為反序列化,就是把存儲或收到的字節(jié)序列信息解析讀取出來使用
(一)XML 序列化
1.準備數據結構
public class Lesson1Test
{
public int testPublic = 10;
private int testPrivate = 11;
protected int testProtected = 12;
internal int testInternal = 13;
public string testPUblicStr = "123";
public int testPro { get; set; }
public Lesson1Test2 testClass = new Lesson1Test2();
public int[] arrayInt = new int[3] { 5, 6, 7 };
public List<int> listInt = new List<int>() { 1, 2, 3, 4 };
public List<Lesson1Test2> listItem = new List<Lesson1Test2>() { new Lesson1Test2(), new Lesson1Test2() };
// 不支持字典
// public Dictionary<int, string> testDic = new Dictionary<int, string>() { { 1, "123" } };
}
public class Lesson1Test2
{
public int test1 = 1;
public float test2 = 1.1f;
public bool test3 = true;
}
Lesson1Test lt = new Lesson1Test();
2.進行序列化
XmlSerializer:用于序列化對象為 xml 的關鍵類
StreamWriter:用于存儲文件
using:用于方便流對象釋放和銷毀
using System.Xml.Serialization;
// 第一步:確定存儲路徑
string path = Application.persistentDataPath + "/Lesson1Test.xml";
// 第二步:結合 using知識點 和 StreamWriter這個流對象 來寫入文件
// 括號內的代碼:寫入一個文件流 如果有該文件 直接打開并修改 如果沒有該文件 直接新建一個文件
// using 的新用法 括號當中包裹的聲明的對象 會在 大括號語句塊結束后 自動釋放掉
// 當語句塊結束 會自動幫助我們調用 對象的 Dispose這個方法 讓其進行銷毀
// using一般都是配合 內存占用比較大 或者 有讀寫操作時 進行使用的
using (StreamWriter stream = new StreamWriter(path)) {
// 第三步:進行xml文件序列化
XmlSerializer s = new XmlSerializer(typeof(Lesson1Test));
// 這句代碼的含義 就是通過序列化對象 對我們類對象進行翻譯 將其翻譯成我們的xml文件 寫入到對應的文件中
// 第一個參數:文件流對象
// 第二個參數:想要備翻譯 的對象
// 注意:翻譯機器的類型 一定要和傳入的對象是一致的 不然會報錯
s.Serialize(stream, lt);
}
3.運行測試
運行后可以看到如下的文件內容(在?path
?文件夾中查看)
可以發(fā)現,只能保存 public 類型的數據
<?xml version="1.0" encoding="utf-8"?>
<Lesson1Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<testPublic>10</testPublic>
<testPUblicStr>123</testPUblicStr>
<testClass>
<test1>1</test1>
<test2>1.1</test2>
<test3>true</test3>
</testClass>
<arrayInt>
<int>5</int>
<int>6</int>
<int>7</int>
</arrayInt>
<listInt>
<int>1</int>
<int>2</int>
<int>3</int>
<int>4</int>
</listInt>
<listItem>
<Lesson1Test2>
<test1>1</test1>
<test2>1.1</test2>
<test3>true</test3>
</Lesson1Test2>
<Lesson1Test2>
<test1>1</test1>
<test2>1.1</test2>
<test3>true</test3>
</Lesson1Test2>
</listItem>
<testPro>0</testPro>
</Lesson1Test>
4.自定義節(jié)點名或設置屬性
public class Lesson1Test
{
[XmlElement("testPublic123123")] // 將該變量對應的結點名字改為 "testPublic123123"
public int testPublic = 10;
private int testPrivate = 11;
protected int testProtected = 12;
internal int testInternal = 13;
public string testPUblicStr = "123";
public int testPro { get; set; }
public Lesson1Test2 testClass = new Lesson1Test2();
public int[] arrayInt = new int[3] { 5, 6, 7 };
[XmlArray("IntList")] // 改變數組對應的結點名字
[XmlArrayItem("Int32")] // 改變數組成員對應的結點名字
public List<int> listInt = new List<int>() { 1, 2, 3, 4 };
public List<Lesson1Test2> listItem = new List<Lesson1Test2>() { new Lesson1Test2(), new Lesson1Test2() };
// 不支持字典
// public Dictionary<int, string> testDic = new Dictionary<int, string>() { { 1, "123" } };
}
public class Lesson1Test2
{
[XmlAttribute("Test1")] // 將該變量存儲為XML屬性,并改名為 "Test1"
public int test1 = 1;
[XmlAttribute] // 將該變量存儲為XML屬性
public float test2 = 1.1f;
[XmlAttribute]
public bool test3 = true;
}
<?xml version="1.0" encoding="utf-8"?>
<Lesson1Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<testPublic>10</testPublic>
<testPUblicStr>123</testPUblicStr>
<testClass Test1="1" test2="1.1" test3="true" />
<arrayInt>
<int>5</int>
<int>6</int>
<int>7</int>
</arrayInt>
<IntList>
<Int32>1</Int32>
<Int32>2</Int32>
<Int32>3</Int32>
<Int32>4</Int32>
</IntList>
<listItem>
<Lesson1Test2 Test1="1" test2="1.1" test3="true" />
<Lesson1Test2 Test1="1" test2="1.1" test3="true" />
</listItem>
<testPro>0</testPro>
</Lesson1Test>
?5. 總結:
- 序列化流程
- 有一個想要保存的類對象
- 使用 XmlSerializer 序列化該對象
- 通過 StreamWriter 配合 using 將數據存儲 寫入文件
- 注意:
- 只能序列化公共成員
- 不支持字典序列化
- 可以通過特性修改節(jié)點信息或者設置屬性信息
- Stream 相關要配合 using 使用
二、XML 反序列化
(一)判斷文件是否存在
using System.IO;
string path = Application.persistentDataPath + "/Lesson1Test.xml";
if(File.Exists(path)) { ... }
(二)反序列化
? 關鍵知識:
- using 和 StreamReader
- XmlSerializer 的 Deserialize 反序列化方法
using System.Xml.Serialization;
// 讀取文件
using (StreamReader reader = new StreamReader(path))
{
// 產生了一個 序列化反序列化的翻譯機器
XmlSerializer s = new XmlSerializer(typeof(Lesson1Test));
Lesson1Test lt = s.Deserialize(reader) as Lesson1Test;
}
? 運行后調試,可以發(fā)現 List 類型的內容被重復添加,原因是變量?lt
?初始化后, List 中有默認值,而反序列化時,Deserialize
?方法會往 List 中用?Add
?方法添加值,而不是覆蓋原有的值。
? 總結:
-
判斷文件是否存在?
File.Exists()
-
文件流獲取?
StreamReader reader = new StreamReader(path)
-
根據文件流 XmlSerializer 通過 Deserialize 反序列化出對象
? 注意:List 對象如果有默認值,反序列化時不會清空,會往后面添加
三、IXmlSerializable 接口
? C# 的 XmlSerializer 提供了可拓展內容,可以讓一些不能被序列化和反序列化的特殊類能被處理
? 讓特殊類繼承?IXmlSerializable 接口,實現其中的方法即可
(一)回顧序列化與反序列化
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class TestLesson3 : IXmlSerializable
{
public int test1;
public string test2;
}
TestLesson3 t = new TestLesson3();
t.test2 = "123";
string path = Application.persistentDataPath + "/TestLesson3.xml";
// 序列化
using (StreamWriter writer = new StreamWriter(path))
{
// 序列化"翻譯機器"
XmlSerializer s = new XmlSerializer(typeof(TestLesson3));
// 在序列化時 如果對象中的引用成員 為空 那么xml里面是看不到該字段的
s.Serialize(writer, t);
}
// 反序列化
using (StreamReader reader = new StreamReader(path))
{
// 序列化"翻譯機器"
XmlSerializer s = new XmlSerializer(typeof(TestLesson3));
TestLesson3 t2 = s.Deserialize(reader) as TestLesson3;
}
<?xml version="1.0" encoding="utf-8"?>
<TestLesson3 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<test1>0</test1>
<test2>123</test2>
</TestLesson3>
(二)繼承 IXmlSerializable 接口
1.繼承接口并實現接口函數
public class TestLesson3 : IXmlSerializable
{
public int test1;
public string test2;
// 返回結構,返回 null 即可,不用過多了解
public XmlSchema GetSchema()
{
return null;
}
// 反序列化時 會自動調用的方法
public void ReadXml(XmlReader reader) { }
// 序列化時 會自動調用的方法
public void WriteXml(XmlWriter writer) { }
}
2.WriteXml
public void WriteXml(XmlWriter writer)
{
// 在里面可以自定義序列化 的規(guī)則
// 如果要自定義 序列化的規(guī)則 一定會用到 XmlWriter中的一些方法 來進行序列化
// 1.寫屬性
writer.WriteAttributeString("test1", this.test1.ToString());
writer.WriteAttributeString("test2", this.test2);
// 2.寫節(jié)點
writer.WriteElementString("test1", this.test1.ToString());
writer.WriteElementString("test2", this.test2);
// 3.寫包裹節(jié)點
XmlSerializer s = new XmlSerializer(typeof(int));
writer.WriteStartElement("test1"); // 寫 <test1>
s.Serialize(writer, test1); // 用序列化翻譯機器寫 test1 的內容
writer.WriteEndElement(); // 寫 </test1>
XmlSerializer s2 = new XmlSerializer(typeof(string));
writer.WriteStartElement("test2"); // 寫 <test2>
s.Serialize(writer, test2); // 用序列化翻譯機器寫 test2 的內容
writer.WriteEndElement(); // 寫 </test2>
}
3.ReadXml
public void ReadXml(XmlReader reader)
{
// 在里面可以自定義反序列化 的規(guī)則
// 1.讀屬性
this.test1 = int.Parse(reader["test1"]);
this.test2 = reader["test2"];
// 2.讀節(jié)點
// 方式一
reader.Read(); // 這時是讀到的test1節(jié)點 <test1>
reader.Read(); // 這時是讀到的test1節(jié)點包裹的內容 0
this.test1 = int.Parse(reader.Value); // 得到當前內容的值=
reader.Read(); // 這時讀到的是尾部包裹節(jié)點 </test1>
reader.Read(); // 這時是讀到的test2節(jié)點 <test2>
reader.Read(); // 這時是讀到的test2節(jié)點包裹的內容 123
this.test2 = reader.Value;
// 方式二
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "test1":
reader.Read();
this.test1 = int.Parse(reader.Value);
break;
case "test2":
reader.Read();
this.test2 = reader.Value;
break;
}
}
}
// 3.讀包裹元素節(jié)點
XmlSerializer s = new XmlSerializer(typeof(int));
XmlSerializer s2 = new XmlSerializer(typeof(string));
reader.Read(); // 跳過根節(jié)點
reader.ReadStartElement("test1"); // 讀 <test1>
test1 = (int)s.Deserialize(reader); // 用反序列化翻譯機器讀 test1 的內容
reader.ReadEndElement(); // 讀 </test1>
reader.ReadStartElement("test2"); // 讀 <test2>
test2 = s2.Deserialize(reader).ToString(); // 用反序列化翻譯機器讀 test2 的內容
reader.ReadEndElement(); // 讀 </test2>
}
四、Dictionary 支持序列化與反序列化
-
我們沒辦法修改 C# 自帶的類
-
那我們可以重寫一個類繼承 Dictionary,然后讓這個類繼承序列化拓展接口 IXmlSerializable
-
實現里面的序列化和反序列化方法即可文章來源:http://www.zghlxwxcb.cn/news/detail-617477.html
public class SerizlizedDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
public XmlSchema GetSchema() {
return null;
}
// 自定義字典的 反序列化 規(guī)則
public void ReadXml(XmlReader reader) {
XmlSerializer keySer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSer = new XmlSerializer(typeof(TValue));
// 要跳過根節(jié)點
reader.Read();
// 判斷 當前不是元素節(jié)點 結束 就進行 反序列化
while (reader.NodeType != XmlNodeType.EndElement) {
// 反序列化鍵
TKey key = (TKey)keySer.Deserialize(reader);
// 反序列化值
TValue value = (TValue)valueSer.Deserialize(reader);
// 存儲到字典中
this.Add(key, value);
}
}
// 自定義 字典的 序列化 規(guī)則
public void WriteXml(XmlWriter writer) {
XmlSerializer keySer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSer = new XmlSerializer(typeof(TValue));
foreach (KeyValuePair<TKey, TValue> kv in this) {
// 鍵值對 的序列化
keySer.Serialize(writer, kv.Key);
valueSer.Serialize(writer, kv.Value);
}
}
}
(一)序列化測試
public class TestLesson4
{
public int test1;
public SerizlizerDictionary<int, string> dic;
}
public class Lesson4 : MonoBehaviour
{
// Start is called before the first frame update
void Start() {
TestLesson4 tl4 = new TestLesson4();
tl4.dic = new SerizlizerDictionary<int, string>();
tl4.dic.Add(1, "123");
tl4.dic.Add(2, "234");
tl4.dic.Add(3, "345");
string path = Application.persistentDataPath + "/TestLesson4.xml";
using (StreamWriter writer = new StreamWriter(path)) {
XmlSerializer s = new XmlSerializer(typeof(TestLesson4));
s.Serialize(writer, tl4);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<TestLesson4 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<test1>0</test1>
<dic>
<int>1</int>
<string>123</string>
<int>2</int>
<string>234</string>
<int>3</int>
<string>345</string>
</dic>
</TestLesson4>
(二)反序列化測試
void Start() {
TestLesson4 tl4 = new TestLesson4();
using (StreamReader reader = new StreamReader(path)) {
XmlSerializer s = new XmlSerializer(typeof(TestLesson4));
tl4 = s.Deserialize(reader) as TestLesson4;
}
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-617477.html
五、自定義 XML 數據管理類
using System;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
public class XmlDataMgr
{
// 單例模式
public static XmlDataMgr Instance { get; } = new XmlDataMgr();
// 防止外部實例化該管理類
private XmlDataMgr() { }
/// <summary>
/// 保存數據到xml文件中
/// </summary>
/// <param name="data">數據對象</param>
/// <param name="fileName">文件名</param>
public void SaveData(object data, string fileName) {
// 1.得到存儲路徑
string path = Application.persistentDataPath + "/" + fileName + ".xml";
// 2.存儲文件
using (StreamWriter writer = new StreamWriter(path)) {
// 3.序列化
XmlSerializer s = new XmlSerializer(data.GetType());
s.Serialize(writer, data);
}
}
/// <summary>
/// 從xml文件中讀取內容
/// </summary>
/// <param name="type">對象類型</param>
/// <param name="fileName">文件名</param>
/// <returns></returns>
public object LoadData(Type type, string fileName) {
// 1.首先要判斷文件是否存在
string path = Application.persistentDataPath + "/" + fileName + ".xml";
if (!File.Exists(path)) {
path = Application.streamingAssetsPath + "/" + fileName + ".xml";
if (!File.Exists(path)) {
// 如果根本不存在文件 兩個路徑都找過了
// 那么直接new 一個對象 返回給外部 無非 里面都是默認值
return Activator.CreateInstance(type);
}
}
// 2.存在就讀取
using (StreamReader reader = new StreamReader(path)) {
// 3.反序列化 取出數據
XmlSerializer s = new XmlSerializer(type);
return s.Deserialize(reader);
}
}
}
if (!File.Exists(path)) {
path = Application.streamingAssetsPath + "/" + fileName + ".xml";
if (!File.Exists(path)) {
// 如果根本不存在文件 兩個路徑都找過了
// 那么直接new 一個對象 返回給外部 無非 里面都是默認值
return Activator.CreateInstance(type);
}
}
// 2.存在就讀取
using (StreamReader reader = new StreamReader(path)) {
// 3.反序列化 取出數據
XmlSerializer s = new XmlSerializer(type);
return s.Deserialize(reader);
}
}
}
到了這里,關于Unity XML3——XML序列化的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!