序列化(Serialization)是指將對象的狀態信息轉換為可以存儲或傳輸的形式(如字節序列、XML 文檔、JSON 字符串等)的過程。反序列化則是序列化的逆過程,它將存儲或接收到的字節序列、XML 文檔、JSON 字符串等轉換回對象的狀態信息。通過序列化,對象可以在不同的環境中進行持久化存儲或網絡傳輸,而反序列化則可以讓接收方恢復出原始的對象。
目錄
常見的序列化方式
1. JSON 序列化
數據類型支持(JsonUtility不支持循環引用)
LitJson
使用場景
1. 數據存儲
2. 網絡通信
3. 配置文件
2.XML 序列化
3. 二進制序列化
常見的序列化方式
1. JSON 序列化
JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,具有良好的可讀性和跨語言性。它以鍵值對的形式組織數據,易于人類閱讀和編寫,也易于機器解析和生成。
Unity 內置了?JsonUtility
?類用于簡單的 JSON 序列化和反序列化,但功能相對有限。可以使用litJson.(LitJson 功能強大但使用相對復雜,適合處理復雜的數據結構和有特殊需求的場景;而 Unity 內置的 JsonUtility 簡單易用,性能較好,適合處理簡單的數據結構。與 Unity 內置的?JsonUtility
?相比,LitJson 在使用時通常不需要為類添加序列化特性)
數據類型支持(JsonUtility
不支持循環引用)
- 基本數據類型:
JsonUtility
?能很好地處理基本數據類型(如果對象之間存在循環引用,JsonUtility
?會拋出異常),如?int
、float
、string
?等。 - 自定義類和結構體:對于自定義類和結構體,類或結構體必須是公共的,且成員變量也必須是公共的,
JsonUtility
?才能正確序列化和反序列化。 - 集合類型:可以處理數組和?
List<T>
?等集合類型,但嵌套集合需要注意結構的正確性。
LitJson
- 強大的類型支持:能處理各種復雜的數據類型,包括嵌套對象、數組、字典等,并且對于自定義類和結構體的處理較為靈活,支持手動實現序列化和反序列化邏輯。
- 自定義序列化:允許開發者自定義序列化和反序列化過程,可通過實現自定義的轉換器來滿足特殊需求,例如對日期格式的自定義處理。
- 處理循環引用:具備一定的處理對象循環引用的能力,雖然可能需要額外的配置,但能應對復雜對象關系的序列化場景。
使用場景
1. 數據存儲
將游戲中的配置數據、玩家進度等存儲為 JSON 文件(實現數據持久化),方便后續讀取和修改。
2. 網絡通信
在網絡通信中(在 Unity C# 的網絡通信中,Newtonsoft.Json
(Json.NET)是使用次數相對較多的序列化方案),將數據序列化為 JSON 格式進行傳輸,接收方再進行反序列化。
3. 配置文件
使用 JSON 文件作為游戲的配置文件,方便開發人員修改和管理。
由于使用這部分的功能在游戲開發過程之中可能是經常需要的,所以我們可以把Json的序列化和反序列化制作成Json模塊的序列化反序列化功能框架。核心功能代碼示例如下:
private static readonly JsonType JsonTypes = JsonType.LitJson;public static void SaveData(object data, string fileName){string path = UnityEngine.Application.persistentDataPath + "/" + fileName + ".json";string jsonStr = "";switch (JsonTypes){case JsonType.JsonUtlity:jsonStr = JsonUtility.ToJson(data);break;case JsonType.LitJson:jsonStr = JsonMapper.ToJson(data);break;}File.WriteAllText(path,jsonStr);}public static T LoadData<T>(string fileName) where T : new(){string path = UnityEngine.Application.streamingAssetsPath + "/" + fileName + ".json";if (!File.Exists(path)) path = UnityEngine.Application.persistentDataPath + "/" + fileName + ".json";if (!File.Exists(path)) return new T();string jsonStr = File.ReadAllText(path);T data = default(T);switch (JsonTypes){case JsonType.JsonUtlity:data = JsonUtility.FromJson<T>(jsonStr);break;case JsonType.LitJson:data = JsonMapper.ToObject<T>(jsonStr);break;}return data;}
2.XML 序列化
XML(eXtensible Markup Language)是一種標記語言,用于存儲和傳輸數據。它具有良好的結構化和擴展性,支持復雜的數據結構和元數據。
xml序列化的使用場景與json的使用場景相似,都是一種數據持久化的序列化方案,我們還可以使用xml制作配置文件書寫一些編輯器小工具的功能。例如生成消息類等。
- 若要使用?
System.Xml.Serialization.XmlSerializer
?對自定義類進行序列化和反序列化,類必須是可序列化的。這意味著類及其所有公共字段都必須是可訪問的,并且類要有無參構造函數。如果類包含私有字段,默認情況下這些字段不會被序列化,除非使用特性(如?[XmlElement]
)進行標記。 - 可以使用 XML 相關的特性(如?
[XmlRoot]
、[XmlElement]
、[XmlAttribute]
?等)來控制 XML 元素和屬性的生成。例如,[XmlRoot]
?可以指定 XML 根元素的名稱,[XmlElement]
?可以指定 XML 元素的名稱,[XmlAttribute]
?可以將字段序列化為 XML 屬性。 - XML 文件的格式必須嚴格遵循 XML 規范,否則在解析時會拋出異常。在讀取和解析 XML 文件時,要進行異常處理,以確保程序的健壯性。
- 不同平臺可能對文件編碼有不同的默認設置,在保存和讀取 XML 文件時,要明確指定編碼格式,以確保數據的正確傳輸和解析。通常建議使用 UTF - 8 編碼。
使用xml制作編輯器小工具的簡單案例:自動生成腳本。(展示一部分)
public void GenerateEnum(XmlNodeList list){string namespaceStr = "";string enumNameStr = "";string fieldStr = "";foreach (XmlNode enumNode in list){//獲取命名空間配置信息namespaceStr = enumNode.Attributes?["namespace"].Value;//獲取枚舉名配置信息enumNameStr = enumNode.Attributes?["name"].Value;XmlNodeList enumFields = enumNode.SelectNodes("field");//一個新的枚舉需要清空上一次拼接的字段字符串fieldStr = "";if (enumFields == null){Debug.Log("不存在field字段,請檢查您的xml配置文件是否準確!");return;}foreach (XmlNode enumField in enumFields){fieldStr += "\t\t" + enumField.Attributes?["name"].Value;if (enumField.InnerText != "")fieldStr += "=" + enumField.InnerText;fieldStr += ",\r\n";}//對所有可變的內容進行拼接string enumStr = $"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic enum {enumNameStr}\r\n" +"\t{\r\n" +$"{fieldStr}" +"\t}\r\n" +"}";//保存文件的路徑string path = _saveDataPath + namespaceStr + "/Enum/";if (!Directory.Exists(path))Directory.CreateDirectory(path);File.WriteAllText(path+enumNameStr+".cs",enumStr);}Debug.Log("枚舉生成結束");}
private static readonly GenerateCSharp generateCSharp = new();private static readonly string protoInfoPath = Application.dataPath + "/Scripts/Game/Editor/XmlTool/Net.xml";[MenuItem("ProtocolTool/生成C#腳本")]private static void GenerateCSharp(){//根據這些信息 拼接字符串 生成對應的腳本//生成對應枚舉腳本generateCSharp.GenerateEnum(GetNodes("enum"));//生成對應的數據結構類腳本generateCSharp.GenerateData(GetNodes("data"));//生成對應消息類腳本generateCSharp.GenerateMsg(GetNodes("message"));//刷新編輯器界面AssetDatabase.Refresh();}
3. 二進制序列化
二進制序列化將對象轉換為字節序列,這種方式通常比文本格式(如 JSON、XML)更緊湊,讀寫速度更快,但可讀性較差。不同的編程語言有不同的二進制序列化機制。(常用于需要高效存儲和傳輸大量數據的場景,如游戲開發、數據庫系統等。)
代碼示例:
/// <summary>/// 在游戲過程中存儲的數據/// </summary>/// <param name="obj">讀取類型</param>/// <param name="isNetworkTransmission">是否使用網絡運輸</param>/// <param name="isEncrypt">是否需要加密數據</param>/// <typeparam name="T"></typeparam>public void AutoCreatFile<T>(T obj,bool isNetworkTransmission=false,bool isEncrypt=true) where T:class{_key = (byte)UnityEngine.Random.Range(1f, 200f);string fileName = typeof(T).Name;if (!Directory.Exists(Application.dataPath + "/BinaryAutoScripts"))_rootFile = Directory.CreateDirectory(Application.dataPath + "/BinaryAutoScripts");if (!_passWordDic.ContainsKey(fileName) && isEncrypt)_passWordDic.Add(fileName,_key);else_passWordDic[fileName] = _key;BinaryFormatter bf = new BinaryFormatter();if (isNetworkTransmission){using var ms = new MemoryStream();bf.Serialize(ms, obj);byte[] bytes = ms.GetBuffer();if (isEncrypt)for (int i = 0; i < bytes.Length; i++)bytes[i] ^= _key;File.WriteAllBytes(Application.dataPath + $"/BinaryAutoScripts/{fileName}_Net.nicolepotter", bytes);}else{using var fs = new FileStream(Application.dataPath + $"/BinaryAutoScripts/{fileName}.nicolepotter", FileMode.OpenOrCreate, FileAccess.Write);bf.Serialize(fs, obj);fs.Flush();}
#if UNITY_EDITORAssetDatabase.Refresh();
#endif}