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

Unity序列化多态数组

文档

Json序列化
脚本序列化

问题

Unity序列化数组时,只能存储基类内容,子类内容缺少。
Unity版本 2019.4.40
原因:Unity序列化不支持多态

测试类
将testarray类序列化时,多态列表personlist只转换了基类数据,子类数据没有转换

    public class Person{public string name = "1";}[System.Serializable]public class Student : Person{public string grade = "2";}[System.Serializable]public class TestArray{public List<Person> personList;}

测试

private void Start()
{TestArray testArray = new TestArray();testArray.personList = new List<Person>() { new Person(), new Student() };var content = JsonUtility.ToJson(testArray);Debug.Log(content);
}

输出结果
student实例的内容grade没有转换

打印:{"personList":[{"name":"1"},{"name":"1"}]}

解决方法

将多态数组拆分为有具体类型的多个数组
相同类型的元素存储到相同类型的数组中,避免多态
优点:思路简单,效率高
缺点:没有了多态的特性

[System.Serializable]
public class Grade3
{public List<Student> students;public List<Person> persons;
}

解决方法1

JsonUtility可以将多态数组的元素完整转换
利用该特点,将多态数组的子元素转存为类型子元素对应的Json字符串
优点:思路简单
缺点:不同类型的多态数组都需要实现转换逻辑,效率低,频繁使用JsonUtility方法GC多
适用于:数据量较少,子类数量不固定

测试类
解释:自定义类实现ISerializationCallbackReceiver接口,
序列化之前,将多态数组中的元素保存为类型和数据字符串
反序列化之后,遍历类型和数据字符串,利用反射和json反序列化,将数据还原到到多态数组中

    [System.Serializable]public class Grade : ISerializationCallbackReceiver{[NonSerialized]public Person[] personList;[SerializeField]private string[] types;[SerializeField]private string[] datas;public void OnBeforeSerialize()//序列化之前{if (personList != null && personList.Length > 0){var length = personList.Length;types = new string[length];datas = new string[length];for (int i = 0; i < length; i++){types[i] = personList[i].GetType().FullName;//存储子元素类型datas[i] = JsonUtility.ToJson(personList[i]);//存储子元素转换的字符串}}}public void OnAfterDeserialize(){if (types != null && datas != null){var length = types.Length;personList = new Person[length];Dictionary<string, Type> typeDic = new Dictionary<string, Type>();//缓存类型避免频繁获取for (int i = 0; i < length; i++){if (!typeDic.ContainsKey(types[i]))typeDic.Add(types[i], Type.GetType(types[i]));var type = typeDic[types[i]];if (type != null && typeof(Person).IsAssignableFrom(type)){Person person = (Person)Activator.CreateInstance(type);//反射创建实例元素JsonUtility.FromJsonOverwrite(datas[i], person);//反序列化数据存放到实例中personList[i] = person;}}}}public void Print()//打印数组元素{for (int i = 0; i < personList.Length; i++){Debug.Log(personList[i]);}}}

测试

private void Start()
{var grade = new Grade();grade.personList = new Person[2] { new Person(), new Student() };var info = JsonUtility.ToJson(grade);Debug.Log(info);
}

输出结果
student实例的内容grade转换

打印:
{
"types":["Test.Person","Test.Student"],
"datas":["{\"name\":\"1\"}","{\"name\":\"1\",\"grade\":\"2\"}"]
}

解决方法2

优点:序列化速度比方法1快,保留多态特性
缺点:子类数量一旦过多,需要编写更多脚本
适用于子类数目较少的,不需要元素的顺序

测试类
解释:序列化时,将多态数组中的数据存放到对应类型的数组中,减少Json方法的调用
反序列化时,将对应数组中的数据存放到多态数组中

    public class Grade2 : ISerializationCallbackReceiver{[NonSerialized]public List<Person> runtimes;//不需要序列化多态数组[SerializeField] Student[] students;[SerializeField] Person[] persons;public void OnBeforeSerialize(){this.students = null;this.persons = null;if (runtimes != null && runtimes.Count > 0){var length = runtimes.Count;int[] indexArray = new int[length];//索引数组int arrayLength = 0;Type targetType = typeof(Student);for (int i = 0; i < length; i++){if (runtimes[i].GetType() == targetType){arrayLength++;indexArray[i] = i;//存放当前Type对应元素的索引}}if (arrayLength > 0){this.students = new Student[arrayLength];//直接设置长度for (int i = 0; i < arrayLength; i++)this.students[i] = runtimes[indexArray[i]] as Student;}arrayLength = 0;targetType = typeof(Person);for (int i = 0; i < length; i++){Type type = runtimes[i].GetType();if (type == targetType){arrayLength++;indexArray[i] = i;}}if (arrayLength > 0){this.persons = new Person[arrayLength];for (int i = 0; i < arrayLength; i++)this.persons[i] = runtimes[indexArray[i]];}}}public void OnAfterDeserialize(){int length = 0;if (students != null)length += students.Length;if (persons != null)length += persons.Length;runtimes = new List<Person>(length);//直接设置列表容量if (students != null)//填充runtimes.AddRange(students);if (persons != null)runtimes.AddRange(persons);}public void Print(){for (int i = 0; i < runtimes.Count; i++){Debug.Log(runtimes[i]);}}}

测试

var grade2 = new Grade2();
grade2.runtimes = new List<Person> { new Person(), new Student() };
var info2 = JsonUtility.ToJson(grade2);
Debug.Log(info2);

输出结果

打印:{"students":[{"name":"1","grade":"2"}],"persons":[{"name":"1"}]}

解决方法3

使用高版本Unity的[SerializeReference]特性
优点:不需要编写多余内容,使用简单,效率比方法1高
缺点:序列化效率差,存储文件大,只支持高版本

测试类

    [System.Serializable]public class Grade4{[SerializeReference]//添加特性public List<Person> runtimes;}

测试

Grade4 grade4 = new Grade4();
grade4.runtimes = new List<Person> { new Person(), new Student() };
var info3 = JsonUtility.ToJson(grade);
Debug.Log(info3);

输出结果

{
"runtimes":[{"rid":1000},{"rid":1001}],
"references":{"version":2,"RefIds":[{"rid":1000,"type":{"class":"Person","ns":"Test","asm":"Assembly-CSharp"},"data":{"name":"1"}},{"rid":1001,"type":{"class":"Student","ns":"Test","asm":"Assembly-CSharp"},"data":{"name":"1","grade":"2"}}]}}

解决方法4

使用Newtonsoft.Json第三方包
优点:功能强大,支持多态数组,使用简单
缺点:第三方导入,效率不高

测试

TestArray testArray5 = new TestArray();
testArray5.personList = new List<Person>() { new Person(), new Student() };JsonSerializerSettings settings = new JsonSerializerSettings();//序列化设置
settings.NullValueHandling = NullValueHandling.Ignore;//空值不保存
settings.TypeNameHandling = TypeNameHandling.Auto;//类型名称自动处理
settings.Formatting = Formatting.Indented;//格式化json文档
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;//忽略循环引用
var content5 = JsonConvert.SerializeObject(testArray, settings);
Debug.Log(content5);

输出结果

打印
{"personList": [{"name": "1"},{"$type": "Test.Student, Assembly-CSharp","grade": "2","name": "1"}]
}

相关文章:

Unity序列化多态数组

文档 Json序列化 脚本序列化 问题 Unity序列化数组时&#xff0c;只能存储基类内容&#xff0c;子类内容缺少。 Unity版本 2019.4.40 原因&#xff1a;Unity序列化不支持多态 测试类 将testarray类序列化时&#xff0c;多态列表personlist只转换了基类数据&#xff0c;子类…...

Spring Framework 中文官方文档

spring的部分中文文档。给总结在下面了&#xff1a; 看英文的大佬可以绕路了哈哈哈 一、 历史、设计理念、反馈、入门。 二、 IoC 容器、事件、资源、i18n、验证、数据绑定、类型转换、SpEL、AOP 三、 模拟对象、TestContext 框架、Spring MVC 测试、WebTestClient。 四、 事…...

力扣-二叉树-257 二叉树的所有路径

思路 除去根节点&#xff0c;每一层添加->val&#xff0c;然后使用前序遍历的顺序 代码 class Solution { public:vector<string> res;void getTreePaths(string s, TreeNode* root){s "->";s to_string(root->val);if(root->left nullptr &…...

如何调整 Nginx工作进程数以提升性能

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Nginx-从零开始的服务器之旅专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年2月15日14点20分 Nginx 的工作进程数&#xff0…...

FreeRTOS-rust食用指南

Rust 环境安装 rustup 是 Rust 的安装程序&#xff0c;也是它的版本管理程序&#xff0c;Linux 命令行下使用如下方式安装 # 安装 rustup curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh #更新 rustup rustup update# 版本检查 rustc -V cargo -VFreeRTOS-rust…...

如何使用智能化RFID管控系统,对涉密物品进行安全有效的管理?

载体主要包括纸质文件、笔记本电脑、优盘、光盘、移动硬盘、打印机、复印机、录音设备等&#xff0c;载体&#xff08;特别是涉密载体&#xff09;是各保密、机要单位保证涉密信息安全、防止涉密信息泄露的重要信息载体。载体管控系统主要采用RFID射频识别及物联网技术&#xf…...

0基础学LabVIEW

对于零基础的朋友来说&#xff0c;学习LabVIEW需要一个科学的学习路径和方法。通过观看优质的B站教程打好基础&#xff0c;再结合实际项目进行实践操作&#xff0c;能够快速提升LabVIEW的应用能力。以下是从入门到进阶的学习建议。 ​ 一、利用B站入门教程打基础 筛选优质教程…...

Go语言精进之路读书笔记(第二部分-项目结构、代码风格与标识符命名)

说明&#xff1a;《Go语言精进之路》第一部分-熟知Go语言的一切&#xff0c;不在博客中做读书笔记了&#xff0c;大家可以自己读一读&#xff0c;每个人心里都会有自己对Go语言的认识和理解。 直接从第二部分-项目接口、代码风格与标识符命名开始 第二章目录如下 第5条 使用…...

Windows server 2016 无法部署docker问题

根据流程winserver16安装docker ee&#xff0c;发现服务器管理器的添加角色和功能-功能中没有 container 根据流程winserver16安装docker desktop&#xff0c;发现安装 hyper-v 报错 原因&#xff1a; 本人测试用windows server 2016使用vmware搭建&#xff0c;而vmware本身可…...

智能AI之隐私安全,尤其是医疗

前言 智能AI能更好的服务我们的生活&#xff0c;各行各业都将会有她的影子。我们在依赖她的情况下&#xff0c;我们的隐私安全吗&#xff1f; 前两天分享了用她分析CT拍片、还有一份血检报告单&#xff0c;回复的确实比有些医生都说的专业全面。以至于我都有冲动依赖她开…...

【hot100】054螺旋矩阵

一、思路 这个题目主要有两个问题&#xff0c;一是什么时候切换方向&#xff0c;二是如何切换方向 问题一&#xff1a;此步移动完后&#xff0c;判断下一个元素&#xff0c;如果大于等于边界值&#xff08;从0开始&#xff09;或者小于边界值时或者访问数组为真时 问题二&am…...

【Java学习】类和对象

目录 一、选择取块解 二、类变量 三、似复刻变量 四、类变量的指向对象 五、变量的解引用访问 1.new 类变量(参) 2.this(参) 3.类变量/似复刻变量. 六、代码块 七、复制变量的赋值顺序 八、访问限定符 1.private 2.default 九、导类 一、选择取块解 解引用都有可以…...

TestHubo基础教程-创建项目

TestHubo是一款国产开源一站式测试工具&#xff0c;涵盖功能测试、接口测试、性能测试&#xff0c;以及 Web 和 App 测试&#xff0c;可以满足不同类型项目的测试需求。本文将介绍如何快速创建第一个项目&#xff0c;以快速入门上手。 1、创建项目 在 TestHubo 中&#xff0c;…...

JS 链表

文章目录 链表题的一些总结两种链表定义set存储链表节点&#xff0c;存的是整个空间同时处理长短不一的两个链表处理方法 while(l1 || l2)处理方法 while(l1 & l2) dummyhead的使用 链表题的一些总结 两种链表定义 class class ListNode {val;next null;constructor(va…...

数据结构(陈越,何钦铭)第三讲 树(上)

3.1 树与数的表示 3.1.1 顺序查找 int SequentialSearch(List Tbl,ElementType K){int i;Tbl->Element[0]K;for(iTbl->Length;Tbl->Element[i]!K;i--);return i; } typedef struct LNode *List; struct LNode{ElementType Element[MAXSIZE];int Length; };3.1.2 二分…...

企业文件安全:零信任架构下的文件访问控制

在企业数字化转型的进程中&#xff0c;传统的文件访问控制模型已难以满足日益复杂的网络安全需求。零信任架构作为一种新兴的安全理念&#xff0c;为企业的文件安全访问提供了全新的解决方案。 一、传统文件访问控制的局限性 传统的文件访问控制主要基于网络边界&#xff0c;…...

性格测评小程序06用户注册校验

目录 1 必填校验2 验证密码强度3 账号唯一性校验最终效果总结 上一篇我们介绍了用户注册的功能&#xff0c;注册的时候对密码进行了加密。除了密码加密外还需要验证账号的唯一性和密码强度的问题&#xff0c;本篇我们介绍一下如何在表单提交的时候增加自定义校验的能力。 1 必填…...

$符(前端)

1‌. jQuery 的别名 用途&#xff1a;$ 是 jQuery 的核心标识符&#xff0c;用于快速选择 DOM 元素或调用 jQuery 方法。 // 选择所有 <div> 元素并隐藏 $(div).hide(); // 发起 AJAX 请求 $.get(/api/data, response > console.log(response)); 注意&#xff1a;虽然…...

Windows 11如何显示全部右键菜单?

在Windows 11系统中&#xff0c;默认的右键菜单进行了简化&#xff0c;若你想要显示全部右键菜单&#xff0c;可以通过以下几种方法实现&#xff1a; 方法一&#xff1a;临时显示完整右键菜单 在需要操作的区域按下Shift键的同时点击鼠标右键&#xff0c;此时弹出的就是完整的…...

离线量化算法和工具 --学习记录1

离线量化算法和工具 一、离线量化的基础概念1.1、基本流程1.2、量化的优点和缺点1.3、如何生产一个硬件能跑的量化模型1.4、PTQ的概念以及和QAT的区别1.5、离线量化的标准流程1.6、校准数据的选择1.7、量化模式的选择1.8、校准方式的选择1.9、量化算法的选择1.10、写入量化参数…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…...