报错问题
ArgumentException: JSON must represent an object type.
报错截图
报错翻译
ArgumentException:JSON必须表示对象类型。
问题分析
Step 1 使用正确json规范
问题定位于
判断出是对于 JsonUtility.FromJson() 理解不够导致出错。
在 Scripting API 在查找该方法
对比代码不难发现:我所用的 JsonUtility.FromJson() 返回的是一个 List 数组,而该方法签名表示返回的类型必须是一个对象。
给出我的“错误的”.json文件
错误Demo示范
[
{
"panelType": "ItemMessage",
"path": "UIPanel/ItemMessagePanel"
},
{
"panelType": "Knapsack",
"path": "UIPanel/KnapsackPanel"
},
{
"panelType": "MainMenu",
"path": "UIPanel/MainMenuPanel"
},
{
"panelType": "Shop",
"path": "UIPanel/ShopPanel"
},
{
"panelType": "Skill",
"path": "UIPanel/SkillPanel"
},
{
"panelType": "System",
"path": "UIPanel/SystemPanel"
},
{
"panelType": "Task",
"path": "UIPanel/TaskPanel"
}
]
不了解json的朋友可以先去看看这位博主关于json的文章
https://blog.csdn.net/yanqing_happy/article/details/98871448
这里我提取出要用到的部分
----JSON 语法规则-----
数据在名称/值对中
数据由逗号分隔
大括号保存对象
中括号保存数组
所以首先需要将json的语法修改为用大括号保存的对象,而不是一个数组!
修改后的json代码如下
{
"infoList": [
{
"panelTypeString": "ItemMessage",
"path": "UIPanel/ItemMessagePanel"
},
{
"panelTypeString": "Knapsack",
"path": "UIPanel/KnapsackPanel"
},
{
"panelTypeString": "MainMenu",
"path": "UIPanel/MainMenuPanel"
},
{
"panelTypeString": "Shop",
"path": "UIPanel/ShopPanel"
},
{
"panelTypeString": "Skill",
"path": "UIPanel/SkillPanel"
},
{
"panelTypeString": "System",
"path": "UIPanel/SystemPanel"
},
{
"panelTypeString": "Task",
"path": "UIPanel/TaskPanel"
}
]
}
也就是用一个对象把这个数组给包装起来啦。
Step 2 使用包装类
使用 JsonUtility 序列化要支持List、Array、Dictionary 最好的方法就是使用包装类
[System.Serializable]
public class UIPanelInfo
{
public UIPanelType panelType;
public string path;
public static UIPanelTypeJson CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson< UIPanelTypeJson >(jsonString);
}
}
public class UIPanelTypeJson // UIPanelInfo 的包装类,无需加 [System.Serializable] 特性
{
public List<UIPanelInfo> infoList;
}
想更多地了解包装类在序列化中的使用可以去看看这位博主的文章
https://blog.csdn.net/oyji1992/article/details/74505230
好,这样我们就完成了对json文件和对包装类的操作。我们可以通过UIPanelTypeJson类 生成的实例来完美地接收
JsonUtility.FromJson()返回的对象类型。
Step 3 使用ISerialiazation接口
但是还没完哦!
注意:
其中UIPanelType 是一个自定义的枚举类型
UIPanelType.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum UIPanelType
{
ItemMessage,
Knapsack,
MainMenu,
Shop,
Skill,
System,
Task
}
此时运行unity会报出下面的错误:
这个问题的报错原因是我们自己定义的类型无法被Unity3D准确地序列化。
Q: 但我们又很想让自己定义的类型能够被准备的序列化,该怎么办呢?
A: 一个可行的想法是将我们的数据类型在要进行序列化时转换为Unity3D能够正确序列化的类型,而在运行时进行反序列化在转换为我们所需要的类型。
这就要用到 ISerialiazationCallbackReceiver 接口
当一个类继承该接口后则必须实现 OnBeforeSerialize() 方法和***OnAfterDeserialize()*** 方法。
OnBeforeSerialize() : Unity3D会在序列化对象之前调用该方法;
OnAfterDeserialize() : Unity3D会在序列化后调用该方法;
代码如下:
[System.Serializable]
public class UIPanelInfo:ISerializationCallbackReceiver
{
[System.NonSerialized]
public UIPanelType panelType;
public string panelTypeString; // 需要转换为 UIPanelType类型的字段,因为unity3D 支持对string类型的序列化
public string path;
public static UIPanelTypeJson CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson< UIPanelTypeJson >(jsonString);
}
public void OnAfterDeserialize()
{
UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
panelType = type;
}
public void OnBeforeSerialize()
{
}
}
public class UIPanelTypeJson // UIPanelInfo 的包装类,无需加 [System.Serializable] 特性
{
public List<UIPanelInfo> infoList;
}
OK,至此,终于解决了这个问题。花了我大半天的时间。
下面推荐一下资源,供大家学习:
bilibili:
https://www.bilibili.com/video/av17222636?p=2
https://www.bilibili.com/video/av33904479?p=23
如果有什么问题或建议,欢迎评论区一起讨论。 —— 一个刚入坑的菜鸡