You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Newtonsoft.Json中结合自定义转换器实现类型解析的问题

解决Unity中Newtonsoft.Json反序列化ScriptableObject的多态与创建冲突问题

问题根源

你的问题出在CustomCreationConverter<T>的实现逻辑上:

  • 转换器泛型参数绑定的是基类T,所以Create方法里返回的CreateInstance<T>()始终是基类实例,直接覆盖了TypeNameHandling解析出的子类类型。
  • 自定义转换器的优先级高于Newtonsoft.Json默认的类型解析逻辑,导致框架直接使用转换器创建的基类对象,不再处理多态类型识别。

解决方案:用自定义JsonConverter替代CustomCreationConverter

直接继承JsonConverter,手动处理类型读取、Unity合法实例创建和属性填充,既能避免new()创建ScriptableObject的报错,又能完整保留多态类型解析能力。

修改后的完整代码

public abstract class Database<T> : ScriptableObject where T : ScriptableObject
{
    public T LoadEntry(string path)
    {
        var settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            Converters = { new ScriptableObjectConverter() }
        };

        string fileContent = File.ReadAllText(path);
        return JsonConvert.DeserializeObject<T>(fileContent, settings);
    }

    private class ScriptableObjectConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            // 只处理T及其子类的ScriptableObject,避免干扰其他类型
            return typeof(T).IsAssignableFrom(objectType) && typeof(ScriptableObject).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // 读取完整的JSON对象
            var jObject = JObject.Load(reader);
            
            // 从JSON的$type字段获取实际子类类型(由TypeNameHandling自动写入)
            var targetType = serializer.TypeNameResolver.ResolveTypeName(jObject["$type"].Value<string>());
            if (targetType == null || !typeof(T).IsAssignableFrom(targetType))
            {
                targetType = objectType;
            }

            // 用Unity合法方式创建对应类型的ScriptableObject实例
            var instance = ScriptableObject.CreateInstance(targetType) as T;
            
            // 将JSON属性填充到已创建的实例中
            serializer.Populate(jObject.CreateReader(), instance);
            
            return instance;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            // 序列化时沿用默认逻辑,保证$type字段正确写入
            serializer.Serialize(writer, value);
        }
    }
}

关键细节说明

  • CanConvert方法:限制转换器的作用范围,只处理T及其子类,避免影响其他类型的序列化流程。
  • 读取实际类型:通过JSON中的$type字段获取序列化时记录的子类类型,确保创建的是目标子类实例而非基类。
  • Unity合法实例创建:使用ScriptableObject.CreateInstance(targetType)替代new(),完全符合Unity的对象生命周期规则,不会触发报错。
  • Populate方法:先创建合法实例,再填充属性,既保留了Newtonsoft.Json的属性解析能力,又规避了直接实例化的问题。

额外注意事项

  • 序列化时必须开启TypeNameHandling(比如TypeNameHandling.Auto或TypeNameHandling.Objects),否则JSON中不会包含$type字段,无法识别子类类型。
  • 如果你的ScriptableObject子类有特殊初始化逻辑,可以在CreateInstance后手动补充处理,常规场景下Populate即可完成所有属性填充。

内容的提问来源于stack exchange,提问作者Whitecold

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:10:05