【问题标题】:Why do I get errors when trying to resolve a derived type during deserialization? (Newtonsoft.Json)为什么在反序列化期间尝试解析派生类型时会出错? (Newtonsoft.Json)
【发布时间】:2019-12-11 07:58:45
【问题描述】:

每当我尝试从 json 反序列化类时,只要序列化数据包含派生类型,就会出错。我的最终目标是能够存储各种派生类型,所以这是一个问题。当我在序列化的对象中仅存储非派生类型时,不会发生该错误。对于添加上下文,代码是使用 Revit 的 AddinManager 扩展运行的加载项的一部分。

(根据建议,我重新编写了代码以尝试隔离问题并给出更易于传达的描述。我已将代码更改为使用两个简单的类。)

更改后我现在得到错误:

JSON 'AddinNamespace.ToyNodePlus, AddinAssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 中指定的类型与'AddinNamespace.ToyNode, AddinAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken'不兼容=空'。路径“TestNode.$type”,第 1 行,位置 61。

插件设置

namespace AddinNamespace
{
    public class AddinSettings
    {   
        public const string Filename = "AddinSettings.json";

        public ToyNode TestNode;
    }
}

ToyNode 和 ToyNodePlus 定义

namespace AddinNamespace
{
    public class ToyNode
    {
        public string Name;
    }

    public class ToyNodePlus : ToyNode
    {
        public int AdditionalValue;
    }
}

序列化代码

private JsonSerializerSettings serializerSettings = null;

private void OnAddinStart(){
    serializerSettings = new JsonSerializerSettings() 
    { 
    TypeNameHandling=TypeNameHandling.Auto,
    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
    };
    if(File.Exists(AddinSettings.Filename)){
        string fileContents = File.ReadAllText(AddinSettings.Filename);
        AddinSettings settings = JsonConvert.DeserializeObject<AddinSettings> (fileContents, serializerSettings);

        //do work with settings here
    }
}

//code for saving to Json (code appears to be working as expected)
private void OnAddinFinish(){
    AddinSettings settings = new AddinSettings();
    settings.TestNode = new ToyNodePlus() {AdditionalValue = 7, Name= "This is a test" };
    string fileContents = JsonConvert.SerializeObject(settings, serializerSettings);
    File.WriteAllText(AddinSettings.Filename, fileContents);
}

创建的 Json 文件内容

{"TestNode":{"$type":"AddinNamespace.ToyNodePlus, AddinAssembly","AdditionalValue":7,"Name":"This is a test"}}

【问题讨论】:

  • 最后一段代码 sn -p 中的设置是什么?
  • 你是如何定义对象列表的?你能发布settings 的结构/类定义吗?
  • 确实,如果您可以包含minimal reproducible example,这样会更容易为您提供帮助。
  • 我已经编辑了这个问题,希望更容易重现。
  • 它仍然不是minimal reproducible example。我不能只是复制、粘贴、编译、运行和查看问题。我可以从你这里得到的 sn-ps 构建所有东西,但是当你可以做到 一次并最终得到一个更清晰的问题。

标签: c# json json.net revit-api


【解决方案1】:

我想我找到了答案。我实际上不得不通过 github 上的source code 找到答案,但我最终能够找出错误发生在哪一行。

当触发以下检查时,JsonSerializerInternalReader.ResolveTypeName(..) 中的检查会引发此特定错误

!objectType.IsAssignableFrom(specifiedType)

调查错误最终发现了这个stackoverflow post。 在我的情况下的具体问题是因为我的代码被作为类库调用并且调用应用程序在第一次调用后将旧的 .dll 文件缓存在临时文件夹中。因为 dll 在技术上是不同的,Type.IsAssignable(Type) 返回 false。

我最终不得不编写一个自定义 SerializationBinder 并将其添加到 JsonSerializerSettings 参数中,以确保使用一致的程序集。 (下面的实现)。我不确定这是否是处理它的最佳方法,所以如果有人有任何替代解决方案或改进......

class CustomSerializationBinder : ISerializationBinder
{
    private Dictionary<string, Assembly> assemblyLookup = new Dictionary<string, Assembly>();
    private Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
    public CustomSerializationBinder(List<Assembly> problemAssemblies = null)
    {
        if (problemAssemblies == null) problemAssemblies = new List<Assembly>();
        foreach(Assembly assembly in problemAssemblies)
        {
            if(!assemblyLookup.ContainsKey(assembly.GetName().Name))
                this.assemblyLookup.Add(assembly.GetName().Name,assembly);
        }
        foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            if(!assemblyLookup.ContainsKey(assembly.GetName().Name))
                assemblyLookup.Add(assembly.GetName().Name, assembly);
        }
    }

    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = serializedType.Assembly.FullName;
        typeName = serializedType.FullName;
    }

    public Type BindToType(string assemblyName, string typeName)
    {
        Type resolvedType;
        Assembly resolvedAssembly;
        typeCache.TryGetValue(typeName, out resolvedType);
        if (resolvedType != null) return resolvedType;

        assemblyLookup.TryGetValue(assemblyName, out resolvedAssembly);
        if (resolvedAssembly == null) return null;
        resolvedType = resolvedAssembly.GetType(typeName);
        if (resolvedType != null)
            typeCache.Add(typeName, resolvedType);
        return resolvedType;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 2013-07-11
    • 2022-01-24
    相关资源
    最近更新 更多