Unity:XML筆記(二)——Xml序列化、反序列化、IXmlSerializable接口

寫在前面:

寫本系列(自用)的目的是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解,方便自己以后快速復習,減少遺忘。

三、Xml序列化

序列化就是把想要存儲的內容轉換為字節序列用于存儲或傳遞。

1、序列化

我們先創建一個類,之后利用Xml序列化來存儲這個類:

public class Test
{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 Test2 testClass = new Test2();public int[] arrayInt = new int[3] { 5, 6, 7 };public List<int> listInt = new List<int>() { 1, 2, 3, 4 };public List<Test2> listItem = new List<Test2>() { new Test2(), new Test2() };
}public class Test2
{public int test1 = 1;public float test2 = 1.1f;public bool test3 = true;
}

Xml序列化的第一步是確認存儲路徑:string path = Application.persistentDataPath + "/Test.xml";該存儲路徑設置方式和之前使用XmlDocument存儲的方式一樣。

序列化存儲需要在一個using代碼塊中,如下:

using (StreamWriter stream = new StreamWriter(path)){? ? ? ? ? }

現在對這行代碼進行解釋。StreamWriter是向文件流寫入字符的類,屬于System.IO命名空間。括號內的代碼的意思是:寫入一個文件流,如果有該文件,直接打開并修改;如果沒有該文件,自動釋放掉。new StreamWriter()
這里還涉及到using 的新用法:括號內包裹的聲明的對象,會在大括號語句塊結束后自動釋放掉。當語句塊結束時會自動調用對象的Dispose方法,讓其銷毀。using一般是配合 內存占用比較大或者有讀寫操作時進行使用。

在語句塊中,我們需要創建一個“序列化機器”來將我們的類序列化:

?XmlSerializer s = new XmlSerializer(typeof(Test));

需要注意的是,序列化機器的類型,一定是要和我們需要序列化存儲的對象是同樣的類型。接下來就可以使用序列化機器進行序列化:

s.Serialize(stream, lt);

這句代碼通過序列化機器,對我們類對象進行翻譯,將其翻譯成xml文件寫入到對應文件中。第一個參數:文件流對象;第二個參數:想要被翻譯的對象。這樣,就完成了序列化存儲:

public class lession1 : MonoBehaviour
{void Start(){Test lt = new Test();string path = Application.persistentDataPath + "/Test.xml";using (StreamWriter stream = new StreamWriter(path)){XmlSerializer s = new XmlSerializer(typeof(Test));s.Serialize(stream, lt);}}
}

我們可以在我們設置的保存路徑中,找到序列化后的Xml文件:

<?xml version="1.0" encoding="utf-8"?>
<Test 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><Test2><test1>1</test1><test2>1.1</test2><test3>true</test3></Test2><Test2><test1>1</test1><test2>1.1</test2><test3>true</test3></Test2></listItem><testPro>0</testPro>
</Test>

可以看到,我們類中private、protected、internal修飾的變量都沒有被序列化。也就是說,Xml序列化方法只能序列化公共成員。此外,不支持字典序列化,如果類中有字典,就會報錯。

2、修改節點信息或設置屬性信息

可以通過特性修改節點信息或者設置屬性信息。例如,如果需要將Test2類中的成員以屬性方式存儲,可以加上特性:[XmlAttribute()],如果想修改屬性的名字,可以在括號內傳入想要的屬性名:[XmlAttribute("Test1")。

public class Test2
{[XmlAttribute("Test1")]public int test1 = 1;[XmlAttribute()]public float test2 = 1.1f;[XmlAttribute()]public bool test3 = true;
}

如果想要修改變量的名字,可以加上特性:[XmlElement("testPublic111")],括號內傳入屬性名:

[XmlElement("testPublic111")]
public int testPublic = 10;

如果想要修改數組的名字,可以添加特性:[XmlArray("IntList")],如果想要修改數組中元素節點的名字,可以用特性:[XmlArrayItem("Int32")]

[XmlArray("IntList")]
[XmlArrayItem("Int32")]
public int[] arrayInt = new int[3] { 5, 6, 7 };

四、Xml反序列化

反序列化就是把存儲或收到的字節序列信息解析讀取出來使用。

1、判斷文件是否存在

在反序列化之前,需要判斷文件是否存在。判斷方式是:File.Exists(path),括號中傳入的是文件地址。

void Start()
{string path = Application.persistentDataPath + "/Test.xml";if(File.Exists(path)){}
}

2、反序列化

反序列化和序列化基本相同,區別就是這里使用的是StreamReader,從流中讀出字符的類。如下:using (StreamReader reader = new StreamReader(path)){? }

同樣的,在using語句塊中,初始化一個反序列化翻譯機器:?XmlSerializer s = new XmlSerializer(typeof(Test));

然后調用s.Deserialize(reader)方法即可完成反序列化:

void Start()
{string path = Application.persistentDataPath + "/Test.xml";if(File.Exists(path)){using (StreamReader reader = new StreamReader(path)){XmlSerializer s = new XmlSerializer(typeof(Test));Test lt = s.Deserialize(reader) as Test;}}
}

這里需要注意的是,在三中定義Test類時,為了方便,很多值都是直接在類中初始化的。對于List對象,如果有默認值,反序列化時不會清空而是會往后繼續添加,所以最好不要在類中直接初始化。

五、IXmlSerializable接口

C#的的XmlSerializer提供了可擴展內容,可以讓一些不能被序列化和反序列化的特殊類能被處理,例如字典。讓特殊類繼承 IXmlSerializable接口實現其中的方法即可。

1、自定義序列化和反序列化

先按三、四所學知識創建一個類并書寫序列化和反序列化方法:

public class Test3
{public int test1;public string test2;
}public class lession3 : MonoBehaviour
{void Start(){Test3 t = new Test3();string path = Application.persistentDataPath + "/Test3.xml";using (StreamWriter writer = new StreamWriter(path)){XmlSerializer s = new XmlSerializer(typeof(Test3));s.Serialize(writer, t);}using(StreamReader reader = new StreamReader(path)){XmlSerializer s = new XmlSerializer(typeof(Test3));Test3 t2 = s.Deserialize(reader) as Test3;}}
}

這段代碼實現了對類Test3進行序列化和反序列化。這里補充一點,在序列化時 如果對象中的引用成員為空 那么xml里面是看不到該字段的,所以這里的xml文件中沒有string。

接下來,我們就可以開始自定義序列化方法和反序列化方法。首先需要類Test3繼承IXmlSerializable接口,并在Test3中重寫接口中的函數:

其中public XmlSchema GetSchema()暫時不需要了解,該函數返回結構,直接return null即可。public void ReadXml(XmlReader reader)是反序列化會調用的方法,在其中書寫的代碼能夠替換掉該類原來的反序列化函數。public void WriteXml(XmlWriter writer)是序列化會調用的方法。這兩個函數可以定義序列化的規則。

public class Test3:IXmlSerializable
{public int test1;public string test2;//返回結構public XmlSchema GetSchema(){return null;}//反序列化時會自動調用方法public void ReadXml(XmlReader reader){}//序列化時會自動調用的方法public void WriteXml(XmlWriter writer){}
}

(1)自定義讀屬性和寫屬性

首先來自定義讀屬性和寫屬性的規則。如果要自定義序列化的規則,一定會用到XmlWriter、XmlReader中的一些方法。

對于寫屬性,XmlWriter是寫入器對象提供一系列方法來生成和寫入 XML 格式的數據。可以使用:writer.WriteAttributeString()寫入屬性,括號內傳入的第一個參數是屬性名,第二個參數是屬性的內容。

對于讀屬性,XmlReader則是用于讀入數據的工具類。可以通過reader["test1"]來獲得[]內屬性的值。如下所示:

public void ReadXml(XmlReader reader)
{this.test1 = int.Parse(reader["test1"]);this.test2 = reader["test2"];    
}public void WriteXml(XmlWriter writer)
{writer.WriteAttributeString("test1", this.test1.ToString());writer.WriteAttributeString("test2", this.test2)
}

(2)自定義讀節點和寫節點

①方式1

寫節點可以通過XmlWriter的方法:writer.WriteElementString(),括號內分別傳入節點名、節點的數值即可。

讀節點需要用reader.Read(),表示逐步讀。一開始Reader位于根節點Test3,調用reader.Read()后讀到test1節點,繼續調用reader.Read()后讀到test1節點包裹的內容,此時就可以將該值讀出來。繼續調用reader.Read()后讀到尾部包裹節點,再調用reader.Read()讀到test2節點...以此類推。

? 這里為了方便看所以給test2賦值為了123再進行讀寫數據。

public void ReadXml(XmlReader reader)
{reader.Read();//這時是讀到的test1節點reader.Read();//這時是讀到的test1節點包裹的內容this.test1 = int.Parse(reader.Value);reader.Read();//尾部包裹節點reader.Read();//這時讀到的是test2節點reader.Read();//讀到的是test2節點包裹的內容this.test2 = reader.Value;
}public void WriteXml(XmlWriter writer)
{writer.WriteElementString("test1", this.test1.ToString());writer.WriteElementString("test2", this.test2);
}
②方式2

方式①讀節點的重復代碼太多了,可以采用方式2這種寫法:

while(reader.Read())
{if(reader.NodeType == XmlNodeType.Element){switch(reader.Name){case "test1":reader.Read();this. test1 = int.Parse(reader.Value);break;case "tese2":reader.Read();this.test2 = reader.Value;break;}}
}

(3)自定義讀寫包裹節點

如果想自定義寫包裹節點,類似于下圖,test1中包裹著節點int,test2中包裹著節點string

①寫

以第一個寫int為例。需要先聲明一個序列化器

XmlSerializer s = new XmlSerializer(typeof(int));

然后使用節點相關API:這兩句代碼表示開始節點test1,結束節點test1,在這兩行中間定義的節點就會被包裹在節點test1中間。

writer.WriteStartElement("test1");writer.WriteEndElement();

例如,在以上兩句代碼中間使用序列化器寫入節點teat的值,序列化器是int類型的,所以會生成<int>0</int>節點:

writer.WriteStartElement("test1");
s.Serialize(writer, test1);
writer.WriteEndElement();
②讀

同樣以讀int為例,首先需要創建一個序列化器:

XmlSerializer s = new XmlSerializer(typeof(int));

調用reader.Read()讓reader指向test1:

reader.Read();

然后可以使用以下兩句代碼,表示讀test1節點的開始于結束:

reader.ReadStartElement("test1");reader.ReadEndElement();

最后在中間寫需要讀入的數據即可:

reader.ReadStartElement("test1");
test1 = (int)s.Deserialize(reader);
reader.ReadEndElement();

完整代碼:

public void ReadXml(XmlReader reader)
{XmlSerializer s = new XmlSerializer(typeof(int));reader.Read();reader.ReadStartElement("test1");test1 = (int)s.Deserialize(reader);reader.ReadEndElement();XmlSerializer s2 = new XmlSerializer(typeof(string));reader.ReadStartElement("test2");test2 = (string)s2.Deserialize(reader);reader.ReadEndElement();
}public void WriteXml(XmlWriter writer)
{XmlSerializer s = new XmlSerializer(typeof(int));writer.WriteStartElement("test1");s.Serialize(writer, test1);writer.WriteEndElement();XmlSerializer s2 = new XmlSerializer(typeof(string));writer.WriteStartElement("test2");s2.Serialize(writer, test2);writer.WriteEndElement();
}

2、讓Dictionary支持序列化反序列化

相當于以上知識的一個小應用,可以拓展一個可以被序列化和反序列化的字典類,所以不多解釋:

public class SerializerDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{public XmlSchema GetSchema(){return null;}public void ReadXml(XmlReader reader){XmlSerializer keySer = new XmlSerializer(typeof(TKey));XmlSerializer ValueSer = new XmlSerializer(typeof(TValue));reader.Read();while(reader.NodeType != XmlNodeType.EndElement){TKey key = (TKey)keySer.Deserialize(reader);TValue value = (TValue)ValueSer.Deserialize(reader);this.Add(key, value);}reader.Read();}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);}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95564.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95564.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95564.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java注解、Lambda表達式、Servlet

一、Java注解注解的概念&#xff1a; Java注解是代碼中的元數據&#xff0c;可以用于描述其他代碼。注解在編譯、類加載、運行時被處理&#xff0c;并且不會改變代碼邏輯。注解的用途&#xff1a; 提供代碼元信息&#xff0c;如 Override 表明一個方法覆蓋了父類的方法。 編譯檢…

【單片機day02】

GPIO&#xff1a;Genral Purpose Input/Output&#xff0c;GPIO是51單片機和外界交互最基本的方式工作模式&#xff1a;輸出模式&#xff1a;單片機給定引腳一個電平(高電平(5V) 低電平(0V)),控制引腳實現高低電平輸入模式&#xff1a;檢測引腳電平變化GPIO水龍頭輸出模式&…

Java中最常用的設計模式

Java設計模式之結構型—代理模式-CSDN博客 觀察者模式詳解-CSDN博客 單例模式詳解-CSDN博客 Java設計模式之結構型—享元模式-CSDN博客 Java設計模式之創建型—建造者模式-CSDN博客 Java設計模式之結構型—工廠模式-CSDN博客 Java設計模式之結構型—適配器模式-CSDN博客 …

使用Axure動態面板制作輪播圖案例詳解

在現代網頁設計中&#xff0c;輪播圖&#xff08;Carousel&#xff09;是一種常見且高效的展示方式&#xff0c;用于在同一空間內循環展示多張圖片或內容。Axure RP作為一款強大的原型設計工具&#xff0c;提供了動態面板和豐富的交互事件功能&#xff0c;使得制作輪播圖變得簡…

VUE的中 computed: { ...mapState([‘auditObj‘]), }寫法詳解

具體解析&#xff1a;computed&#xff1a;這是 Vue 組件選項中的計算屬性&#xff0c;用于聲明依賴于其他數據而存在的派生數據。計算屬性會根據依賴進行緩存&#xff0c;只有當依賴的數據發生變化時才會重新計算。mapState&#xff1a;這是 Vuex 提供的一個輔助函數&#xff…

【ProtoBuf】以 “數據秘語” 筑聯絡:通訊錄項目實戰 1.0 啟步札記

文章目錄引言筑路之備&#xff1a;快速上手ProtoBuf步驟一&#xff1a;創建.proto文件?件規范添加注釋指定 proto3 語法package 聲明符定義消息&#xff08;message&#xff09;定義消息字段【定義聯系人 message】字段唯一編號的范圍步驟2&#xff1a;編譯 contacts.proto ?…

在 macOS 下升級 Python 幾種常見的方法

在 macOS 下升級 Python 有幾種常見的方法&#xff0c;具體取決于你最初是如何安裝 Python 的。了解你的安裝方式是關鍵。 首先&#xff0c;你需要知道你當前 Python 版本以及它的安裝路徑。 檢查 Python 版本&#xff1a; python --version # 可能指向 Python 2.x python3 …

Linux 入門到精通,真的不用背命令!零基礎小白靠「場景化學習法」,3 個月拿下運維 offer,第二十五天

三、Shell腳本編程 Shell腳本語言的運算 算數運算 shell支持算術運算&#xff0c;但只支持整數&#xff0c;不支持小數 Bash中的算術運算 -- 加法運算 -- - 減法運算 -- * 乘法運算 -- / 除法運算 -- % 取模&#xff0c;即取余數 -- ** 乘方 ? #乘法符號在有些場景需要轉…

SpringAI系列---【多租戶記憶和淘汰策略】

1.多租戶工作原理 2.引入jdbc的pom spring官網鏈接&#xff1a;https://docs.spring.io/spring-ai/reference/api/chat-memory.html&#xff0c;推薦使用官網的jdbc。 阿里巴巴ai鏈接&#xff1a;https://github.com/alibaba/spring-ai-alibaba/tree/main/community/memories j…

Linux gzip 命令詳解:從基礎到高級用法

Linux gzip 命令詳解&#xff1a;從基礎到高級用法 在 Linux 系統中&#xff0c;文件壓縮與解壓縮是日常運維和文件管理的常見操作。gzip&#xff08;GNU Zip&#xff09;作為一款經典的壓縮工具&#xff0c;憑借其高效的壓縮算法和簡潔的使用方式&#xff0c;成為 Linux 用戶處…

Redis有什么優點和缺點?

優點&#xff1a;極致性能&#xff1a; 基于內存操作和高效的單線程 I/O 模型&#xff0c;讀寫速度極快。數據結構豐富&#xff1a; 支持多種數據結構&#xff0c;如 String、Hash、List、Set、ZSet、Stream、Geo 等&#xff0c;編程模型靈活。持久化與高可用&#xff1a; 提供…

NestJS 3 分鐘搭好 MySQL + MongoDB,CRUD 復制粘貼直接運行

基于上一篇內容《為什么現代 Node 后端都選 NestJS TypeScript&#xff1f;這組合真香了》&#xff0c;這篇文章繼續寫數據庫的連接。 所以今天把MySQL、MongoDB全接上&#xff0c;做個小實例。朋友們項目里用什么數據庫可以視情況而定。 這里的功能分別為&#xff1a; MySQ…

用了企業微信 AI 半年,這 5 個功能讓我徹底告別重復勞動

每天上班不是在整理會議紀要&#xff0c;就是在翻聊天記錄找文件&#xff0c;寫文檔還要自己摳數據…… 這些重復勞動是不是也在消耗你的時間&#xff1f;作為用了企業微信 AI 功能半年的 “老用戶”&#xff0c;我必須說&#xff1a;企業微信 AI 的這 5 個功能&#xff0c;真的…

從入門到高手,Linux就應該這樣學【好書推薦】

從入門到高手&#xff0c;請這樣學Linux 一、Linux基礎與終端操作 1.1 Linux簡介 Linux 是一種開源的類 Unix 操作系統&#xff0c;以其穩定性、安全性和高效性被廣泛應用于服務器、嵌入式系統及開發環境中。掌握基本命令和操作技巧是 Linux 學習的關鍵。 1.2 終端基礎 打開…

【數據可視化-104】安徽省2025年上半年GDP數據可視化分析:用Python和Pyecharts打造炫酷大屏

&#x1f9d1; 博主簡介&#xff1a;曾任某智慧城市類企業算法總監&#xff0c;目前在美國市場的物流公司從事高級算法工程師一職&#xff0c;深耕人工智能領域&#xff0c;精通python數據挖掘、可視化、機器學習等&#xff0c;發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN…

組件庫UI自動化

一、背景 背景&#xff1a; 組件庫全局改動場景多&#xff0c;組件之間耦合場景多–時常需要全場景回歸組件庫demo有200多個&#xff0c;手動全局回歸耗時耗力細微偏差純視覺無法辨別 可行性分析&#xff1a; 組件庫功能占比 L1&#xff08;視覺層&#xff09;&#xff1a;圖片…

面試題:JVM與G1要點總結

一.Java內存區域 1.運行時數據區的介紹 2.站在線程的角度看Java內存區域 3.深入分析堆和棧的區別 4.方法的出入棧和棧上分配、逃逸分析及TLAB 5.虛擬機中的對象創建步驟 6.對象的內存布局 1.運行時數據區的介紹 運行時數據區的類型&#xff1a;程序計數器、Java虛擬機棧、本地方…

車輛安全供電系統開發原則和實踐

摘要在汽車行業中&#xff0c;安全應用的重要性在不斷提升&#xff0c;例如受車輛自動化發展以及機械備用系統重要性降低的影響。為應對這些趨勢&#xff0c;安全相關的電氣和 / 或電子系統&#xff08;E/E 系統&#xff09;的電源輸入必須由供電系統來保障&#xff0c;這使得功…

WebSocket客戶端庫:websocket-fruge365

&#x1f680; 從零開始打造一個WebSocket客戶端庫&#xff1a;websocket-fruge365 &#x1f4d6; 前言 在現代Web開發中&#xff0c;實時通信已經成為不可或缺的功能。無論是聊天應用、實時數據監控&#xff0c;還是在線協作工具&#xff0c;WebSocket都扮演著重要角色。然而…

rocketmq批量執行跑批任務報錯

rocketmq批量執行跑批任務&#xff0c;報下面的錯誤&#xff0c;怎么處理一下呢&#xff1f;是修改配置還是修改代碼還是&#xff1f; org.apache.rocketmq.client.exception.MQBrokerException: CODE: 215 DESC: [FLOW]client has exhausted the send quota for the current …