如何使用 System.Text.Json API 将流反序列化为对象
Posted
技术标签:
【中文标题】如何使用 System.Text.Json API 将流反序列化为对象【英文标题】:How to deserialize stream to object using System.Text.Json APIs 【发布时间】:2020-02-19 02:00:12 【问题描述】:我正在以流形式接收来自 Web api 调用的响应,需要将其反序列化为模型。
这是一个通用方法,所以我不能说代码的哪些部分会使用它以及响应负载是什么。
方法如下:
public async Task<T> InvokeAsync<T>(string method)
Stream response = await this.httpClientWrapper.InvokeAsync(method);
var serializer = new JsonSerializer();
using var streamReader = new StreamReader(response);
using var reader = new JsonTextReader(streamReader);
return serializer.Deserialize<T>(reader);
我正在尝试删除 Newtonsoft 并使用 System.Text.Json API。
我在 Github 的 corefx 存储库中找到了这个 porting guide,其中 Reading from a Stream/String 部分指出:
我们目前(截至 .NET Core 3.0 预览版 2)没有方便的 直接从流中读取 JSON 的 API(同步或 异步)。对于同步阅读(尤其是小 有效负载),您可以读取 JSON 有效负载直到流结束 放入一个字节数组并将其传递给阅读器
因此,按照这个建议,我提出了以下建议:
public async Task<T> InvokeAsync<T>(string method)
Stream response = await this.httpClientWrapper.InvokeAsync(method);
var length = response.Length;
var buffer = ArrayPool<byte>.Shared.Rent((int)length);
var memory = new Memory<byte>(buffer);
await response.WriteAsync(memory);
var result = JsonSerializer.Deserialize<T>(memory.Span);
ArrayPool<byte>.Shared.Return(buffer);
return result;
所以我的问题是 - 我是否正确理解了建议并且这是要走的路?
这个实现可能在很多方面都可以改进,但最困扰我的是从池中租用字节数组,例如Stream.Length
很长,我将其转换为 int 可能会导致 OverflowException
。
我试图研究 System.IO.Pipelines 并使用 JSON API 的 ReadOnlySequence<byte>
重载,但它变得非常复杂。
【问题讨论】:
【参考方案1】:我认为文档需要更新,因为 .NET Core 3 有一个 method 可以直接从流中读取。使用它很简单,假设流是用 UTF8 编码的:
private static readonly JsonSerializerOptions Options = new JsonSerializerOptions();
private static async Task<T> Deserialize<T>(HttpResponseMessage response)
var contentStream = await response.Content.ReadAsStreamAsync();
var result = await JsonSerializer.DeserializeAsync<T>(contentStream, Options);
return result;
需要注意的一点是,默认情况下,HttpClient 会在返回之前将响应内容缓存在内存中,除非您在调用 SendAsync 时将 HttpCompletionOption 设置为 ResponseHeadersRead:
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
【讨论】:
感谢您的回复。后来我看到了这个 API,但现在我在质疑更大的有效载荷。分割整个流并读取缓冲区由JsonTextReader
完成。 System.Text.Json
API 似乎不是这种情况,但这是另一个问题。
嗨@Mike,你找到更大有效载荷的解决方案了吗?
@DirkBoer 不确定您使用的是哪个 .NET Core 版本,但在我们的例子中,我们使用的是 .NET Core 3.1 并且来自流的DeserializeAsync
的实现负责chopping up the stream to buffers and reading from them。 以上是关于如何使用 System.Text.Json API 将流反序列化为对象的主要内容,如果未能解决你的问题,请参考以下文章
将 false 反序列化为 null (System.Text.Json)
System.Text.Json API 有类似 IContractResolver 的东西
System.Text.Json 反序列化来自 API 调用的嵌套对象 - 数据包装在父 JSON 属性中