忽略缺失类型在反序列化列表期间。

4
当使用TypeNameHandling.All反序列化列表时,如果其中一项的类型命名空间缺失(在序列化后被删除),则会导致Error resolving type specified in JSON错误。我希望忽略这些项,并将其余部分留下。
JsonSerializerSettings中设置Error = (sender, args) => { args.ErrorContext.Handled = true; }可以实现此目的,但它会捕获所有的错误。
是否有更简单的方法来做到这一点,也许是通过我遗漏的序列化器设置?
2个回答

6
您可以在SerializationErrorCallback中使用以下ErrorContext属性来限制要处理和忽略的错误类型:
  • ErrorEventArgs.ErrorContext.OriginalObject:此项获取导致错误的原始对象。当无法构建列表项时因为类型名称无效,OriginalObject将是列表本身。

    使用此属性,您可以检查OriginalObject是否为某个TIList<T>

  • ErrorEventArgs.CurrentObject。获取引发错误事件的当前对象。异常会沿着序列化调用堆栈向上传播,并且每个级别的对象都可以尝试处理错误。

    您将希望在最低级别处理它,即CurrentObject == ErrorContext.OriginalObject时。

  • ErrorEventArgs.ErrorContext.Error - 获取实际抛出的异常。您将只希望处理序列化绑定器引发的异常。

现在,如何仅检测和捕获由于类型名称绑定失败而引起的异常?事实证明,Json.NET的DefaultSerializationBinder在无法加载类型时会引发JsonSerializationException。然而,相同的异常也可能在许多其他情况下引发,包括格式不正确的JSON文件。因此,引入一个装饰器ISerializationBinder来捕获并捕获默认JSON绑定器的异常,并将它们打包成特定的异常类型:

public class JsonSerializationBinder : ISerializationBinder
{
    readonly ISerializationBinder binder;

    public JsonSerializationBinder(ISerializationBinder binder)
    {
        if (binder == null)
            throw new ArgumentNullException();
        this.binder = binder;
    }

    public Type BindToType(string assemblyName, string typeName)
    {
        try
        {
            return binder.BindToType(assemblyName, typeName);
        }
        catch (Exception ex)
        {
            throw new JsonSerializationBinderException(ex.Message, ex);
        }
    }

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

public class JsonSerializationBinderException : JsonSerializationException
{
    public JsonSerializationBinderException() { }

    public JsonSerializationBinderException(string message) : base(message) { }

    public JsonSerializationBinderException(string message, Exception innerException) : base(message, innerException) { }

    public JsonSerializationBinderException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

此外,在代码的某个更高级别处,Json.NET将JsonSerializationBinderException包装在另一个JsonSerializationException中,因此在决定是否处理异常时,需要查看内部异常中是否有必要类型的异常。以下设置可以完成此操作:

var settings = new JsonSerializerSettings
{
    SerializationBinder = new JsonSerializationBinder(new DefaultSerializationBinder()),
    TypeNameHandling = TypeNameHandling.All, // Or Auto or Objects as appropriate
    Error = (sender, args) =>
    {
        if (args.CurrentObject == args.ErrorContext.OriginalObject
            && args.ErrorContext.Error.InnerExceptionsAndSelf().OfType<JsonSerializationBinderException>().Any()
            && args.ErrorContext.OriginalObject.GetType().GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList<>)))
        {
            args.ErrorContext.Handled = true;
        }
    },
};

使用扩展方法:
public static class ExceptionExtensions
{
    public static IEnumerable<Exception> InnerExceptionsAndSelf(this Exception ex)
    {
        while (ex != null)
        {
            yield return ex;
            ex = ex.InnerException;
        }
    }
}

这里是演示fiddle #1 链接

请注意,ISerializationBinder是在Json.NET 10.0.1中引入的。 在早期版本中,您的包装器必须继承自SerializationBinder并在JsonSerializerSettings.Binder中设置。

这里是演示fiddle #2 链接


0

我曾经遇到过类似的问题,与反序列化时缺少类型有关。我按照@dbc建议的解决方案CustomSerializationBinder进行了操作。

但是,这种方法只适用于缺少类型的对象在List中的情况。 如果你的缺少类型的对象是Dictionary,这种方法将不起作用。

//This works
{
  "nodeList": [
    {
      "$type": "Leopotam.Ecs.BT.BTRuntimeAction, Assembly-CSharp",
      "taskList": []
    },
    {
      "$type": "Leopotam.Ecs.LancerGirl.BTTask.AlterResult, Assembly-CSharp", //Missing
      "alterResult": 0
    }
  ]
}

//This will not work
{
  "nodeDic": {
    "0": {
      "$type": "Leopotam.Ecs.BT.BTRuntimeSequence, Assembly-CSharp",
      "id": 0
    },
    "1": {
      "$type": "Leopotam.Ecs.BT.BTRuntimeAction, Assembly-CSharp", // Missing
      "taskList": []
    }
  }
}

如果在字典中缺少类型(例如BTRuntimeAction),并且您使用代码“args.ErrorContext.Handled = true;”跳过错误,则会出现下一个错误,该错误将说“解析错误,带有错误标记”,无法解析“taskList”元素。Json应该忽略整个缺失类型的字符串部分,但它只忽略一行,并解析下一行,导致错误。
所以...只需使用列表即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接