如何使JsonSerializer.DeserializeAsyncEnumerable异步返回可用部分结果?
问题分析与解决方案
核心问题
你遇到的是.NET中JsonSerializer.DeserializeAsyncEnumerable的回归bug(对应GitHub issue #51702),该bug导致方法在流未完全读取前不会返回任何元素,无法实现预期的JSON流式反序列化——也就是没法在部分结果可用时就异步返回枚举内容。
直接修复:升级.NET版本
这个bug已在特定.NET版本中修复,只需确保项目使用包含该修复的版本即可:
- .NET 6:升级至6.0.12及以上
- .NET 7:升级至7.0.1及以上
- .NET 8及后续版本:默认已集成修复
升级完成后,DeserializeAsyncEnumerable会恢复预期行为:每当流中接收到完整的单个SearchName对象时,就会立即返回该元素,无需等待整个流传输完成。
临时替代方案(无法升级版本时)
如果暂时无法升级.NET版本,可以通过以下两种方式实现流式解析:
方案1:手动处理分块JSON
假设服务器返回的是标准JSON数组格式(如[{}, {}, ...]),可以逐段读取流并识别单个对象的边界,再反序列化单个对象:
using var reader = new StreamReader(outStream); var buffer = new char[4096]; var jsonFragment = new StringBuilder(); var inArray = false; var objectDepth = 0; while (await reader.ReadAsync(buffer, 0, buffer.Length) > 0) { foreach (var c in buffer) { if (c == '[') inArray = true; if (!inArray) continue; jsonFragment.Append(c); if (c == '{') objectDepth++; else if (c == '}') objectDepth--; // 当单个对象闭合时,反序列化并重置片段 if (objectDepth == 0 && c == '}') { var name = JsonSerializer.Deserialize<SearchName>(jsonFragment.ToString(), opts); if (name != null) { Console.WriteLine(Uri.UnescapeDataString(name.familyName)); } jsonFragment.Clear(); // 跳过后续的逗号和空格 while (reader.Peek() is ',' or ' ') reader.Read(); } } }
方案2:使用第三方流式JSON库
借助Newtonsoft.Json(Json.NET)的JsonTextReader实现原生流式解析,逐个读取并反序列化JSON对象:
using var reader = new JsonTextReader(new StreamReader(outStream)); var serializer = new JsonSerializer(); // 跳过数组起始符号 reader.Read(); while (reader.Read() && reader.TokenType != JsonToken.EndArray) { if (reader.TokenType == JsonToken.StartObject) { var name = serializer.Deserialize<SearchName>(reader); if (name != null) { Console.WriteLine(Uri.UnescapeDataString(name.familyName)); } } }
验证流式效果
修复或替换实现后,可以通过以下方式验证是否真正实现流式:
- 在服务器端用
yield return逐个返回SearchName对象 - 观察客户端控制台是否逐行打印结果,而非等全部数据传输完成后一次性输出
内容的提问来源于stack exchange,提问作者McMurphy
相关产品推荐
相关产品推荐

