在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
相关产品推荐
相关产品推荐

