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



使用TypeNameHandling.All对列表进行反序列化时,如果其中一项的类型名称空间缺失(序列化后删除),则会导致Error resolving type specified in JSON错误。
我希望忽略这些项目,而把其余的留在后面。

JsonSerializerSettings中的Error = (sender, args) => { args.ErrorContext.Handled = true; }做我正在寻找的,但当然会捕获所有错误。

是否有一种更干净的方法来做到这一点,也许是通过我错过的序列化器设置?

您可以在SerializationErrorCallback中使用ErrorContext的以下属性来限制要处理和忽略的错误类型:

  • ErrorEventArgs.ErrorContext.OriginalObject: this 获取导致错误的原始对象。当由于类型名无效而无法构造列表项时,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;
        }
    }
}

在这里演示小提琴#1。

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

我也遇到过类似的问题,这与反序列化时缺少type有关。我遵循了@dbc建议的解决方案CustomSerializationBinder。

但这只适用于当你的missingType是在List。如果你的Dictionary对象是missingType,这个方法将不起作用。

//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应该忽略missingType的整个section字符串,但它只忽略一行,并解析下一行,导致错误。

所以…使用list

相关内容

  • 没有找到相关文章

最新更新