Json.Net 中的“当前错误上下文错误与请求的错误不同”异常

Posted

技术标签:

【中文标题】Json.Net 中的“当前错误上下文错误与请求的错误不同”异常【英文标题】:"Current error context error is different to requested error" exception in Json.Net 【发布时间】:2015-12-21 12:45:40 【问题描述】:

上下文

我用 C# 编写了一个并行作业框架,用于从 ElasticSearch 集群导入/导出大量数据。为此,我将单个项目的每次导入或导出建模为在某个时间由框架执行的对象。为了与 ElasticSearch 交互,我使用 NEST(官方 .NET ElasticSearch 客户端库)v1.7.1 和 JSON.Net 7.0.1。

每个导入/导出任务对象都使用 NEST 与 ElasticSearch 交互。出于性能原因,我编写了一个代理类,它将任务对象生成的搜索请求分组为固定大小的批次,以与 NEST 的 _msearch API 一起使用。这个类的调用者被延迟到它的批处理返回。 That class is available here.

我的框架将每个导入/导出任务的结果模型包装为“bool”或“Exception”。即使遇到个别项目的错误,整个过程也能够继续。

问题

在完成几个小时的任务后,我看到以下异常被引发了数千次:

System.InvalidOperationException: Current error context error is different to requested error.
    at _____.Matcher.<GetBestMatchAsync>d__15.MoveNext() in C:\\_work\\edc7a363\\_____\\Matcher.cs:line 266
    --- End of stack trace from previous location where exception was thrown ---
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
    _____.MatchBlock`1.<ExecuteAsyncInternal>d__19.MoveNext() in C:\\_work\\edc7a363\\_____\\MatchBlock.cs:line 111
    --- End of stack trace from previous location where exception was thrown ---
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
    at _____.Block.BlockBase.<ExecuteAsync>d__11.MoveNext() in C:\\_work\\edc7a363\\_____\\Block\\BlockBase.cs:line 33

这是引发异常的代码(来自上面链接的批量搜索器类):

try

    var bulkResponse = Client.MultiSearch(searchDescriptor);
    var items = bulkResponse.GetResponses<T>().ToList();

    // Set response values and release all waiting tasks
    var zip = currentBuffer.Zip(items, (op, result) => new  op, result );
    foreach (var a in zip)
    
        a.op.Response = a.result;
        a.op.Cts.Cancel();
    

catch (Exception e)

    foreach (var op in currentBuffer)
    
        op.Error = e;
        op.Cts.Cancel();
    

其中ClientIElasticClient

谷歌搜索异常消息将我带到this method in the JsonSerializerInternalBase class in JSON.Net,这似乎在每次反序列化后执行:

private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error)

    if (_currentErrorContext == null)
    
        _currentErrorContext = new ErrorContext(currentObject, member, path, error);
    

    if (_currentErrorContext.Error != error)
    
        throw new InvalidOperationException("Current error context error is different to requested error.");
    

    return _currentErrorContext;

鉴于单个 NEST 对象被跨多个线程的每个操作重用 - 我认为 NEST 只使用一个 JsonSerializer 实例 - 这让我认为 JSON.Net 的这一部分不是线程安全的。虽然奇怪的是直到运行几个小时后才开始发生错误。

如何进一步调试?

【问题讨论】:

JsonSerializerInternalBaseJsonSerializerInternalWriter.csJsonSerializerInternalReader.cs 的基类,而不是 JsonSerializer。对于对SerializeDeserializePopulate 的每次调用,JsonSerializer 分配其中一个来执行实际工作——可能是为了线程安全。 1) 回溯看起来不完整——它没有显示 Json.NET 本身内部的任何回溯。您可以编辑问题以包含整个回溯,包括所有内部异常吗? 2) 能否显示对serializer.Serialize() 的调用,包括如何初始化JsonSerializer.Error 1.该堆栈跟踪是我所拥有的。来自批量搜索器类的代码块将异常复制出来并在调用者线程上重新抛出;我猜它会丢失完整的堆栈跟踪。我将更改代码以生成完整的跟踪并在一夜之间运行它。 2. NEST 创建了它自己的 JsonSerializer,尽管我使用了一个调用不同 JsonSerializer 的自定义 JsonConverter,这很复杂。这里有很多代码,所以我将等待重新运行的结果,然后再开始为 *** 格式化它。感谢您的提问 【参考方案1】:

我的同事最终追查到错误 - 这是由于从另一个 JsonConverter 调用的 JsonConverter 内部引发了异常。 “错误上下文”是用于跟踪上次抛出的异常的内部 JSON.Net 事物。似乎异常是由错误的 JsonConverter 处理的。我们在内部 JsonConverter 中添加了一个标志,让它知道在特定上下文中不要抛出异常。

【讨论】:

【参考方案2】:

当使用自定义序列化器将枚举属性序列化为 null,然后使用默认反序列化器反序列化时,我遇到了此错误。

【讨论】:

【参考方案3】:

我发现如果您像这样使用JsonSerializerSettings 错误处理程序,也会发生这种情况:

parsedResponse = JsonConvert.DeserializeObject<T>(
  json,
  new JsonSerializerSettings
  
    Error = (object sender, ErrorEventArgs args) =>
    
      throw new MyCustomException(String.Format("Parse error: 0", args.ErrorContext.Error.Message));
    ,
...

抛出的错误不会是MyCustomException,而是“当前错误上下文错误与请求错误不同”。如果您在错误处理程序中抛出异常,显然 Json.NET 不喜欢它

【讨论】:

以上是关于Json.Net 中的“当前错误上下文错误与请求的错误不同”异常的主要内容,如果未能解决你的问题,请参考以下文章

使用 Json.Net 处理数据中的换行符

忽略 Json.net 中的空字段

确保 .NET 中的 json 键是小写的

.NET 中的 LINQ to JSON

Json.Net 中的 PreserveReferencesHandling 和 ReferenceLoopHandling 有啥区别?

使用 json.net 中的属性序列化 DataSet/DataTable