当前位置: 首页 > article >正文

序列化是什么?常见的序列化方式有哪些?什么时候我们会用到序列化?

序列化(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 会抛出异常),如 intfloatstring 等。
  • 自定义类和结构体:对于自定义类和结构体,类或结构体必须是公共的,且成员变量也必须是公共的,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_EDITOR
            AssetDatabase.Refresh();
#endif
        }

http://www.kler.cn/a/563744.html

相关文章:

  • 【STM32】玩转IIC之驱动MPU6050及姿态解算
  • 解决PyCharm工程中pip版本和python中的pip版本不一致
  • 基于 spring boot+vue 的仓储管理系统 的设计与实现
  • 自然语言处理:初识自然语言处理
  • C++ 正则表达式分组捕获入门指南
  • 在vscode中编译运行c语言文件,配置并运行OpenMP多线程并行程序设计
  • 爬虫抓取数据时如何处理异常?
  • C++类与对象深度解析(二):从const 、new 、delete到函数操作符与友元函数的编程实践
  • 蓝桥杯练习代码
  • CSS基础选择器和文字属性控制
  • [U-BOOT][STM32]设置使用SD卡中的linux程序启动
  • 怎样能写出完美的Prompt
  • 鸿蒙中连接手机可能遇到的问题
  • 鸿蒙-AVPlayer
  • SpringBoot整合Mybatis-Plus+Druid实现多数据源
  • MySQL -安装与初识
  • 【linux】文件与目录命令 - sed
  • 基于Flask框架生产环境快速部署
  • React + TypeScript 数据模型驱动数据字典生成示例
  • word中对插入的图片修改背景色