如何使用 System.Text.Json API 将流反序列化为对象 [英] How to deserialize stream to object using System.Text.Json APIs
问题描述
我收到来自 web api 调用的响应作为流,需要将其反序列化为模型.
I'm receiving a response from a web api call as a stream and need to deserialize it to a model.
这是一个通用方法,所以我不能说代码的哪些部分会使用它以及响应负载是什么.
This is a generic method, so I can't say which parts of code will use this and what's the response payload.
方法如下:
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.
I'm trying to remove Newtonsoft and use System.Text.Json API.
我发现了这个移植指南 在 Github 的 corefx 存储库中,其中部分 从流/字符串中读取 状态:
I found this porting guide in corefx repo in Github, where section Reading from a Stream/String states:
我们目前(从 .NET Core 3.0 预览版 2 开始)没有一个方便的直接从流中读取 JSON 的 API(同步或异步).用于同步读取(尤其是小有效载荷),您可以读取 JSON 有效载荷直到流结束成一个字节数组并将其传递给阅读器
We currently (as of .NET Core 3.0 preview 2) do not have a convenient API to read JSON from a stream directly (either synchronously or asynchronously). For synchronous reading (especially of small payloads), you could read the JSON payload till the end of the stream into a byte array and pass that into the reader
所以按照这个建议,我想出了以下几点:
So following this advise I come up with the following:
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;
}
所以我的问题是 - 我是否正确理解了建议,这是要走的路?
So my question is - did I understand the advise correctly and this is the way to go ?
这个实现可能在很多方面都可以改进,但最让我烦恼的是从池中租用字节数组,例如Stream.Length
是一个 long,我将它转换为 int,这会导致 OverflowException
.
This implementation probably can be improved on many aspects, but what bothers me most is renting the byte array from the pool e.g. Stream.Length
is a long and I convert it to int which can cause OverflowException
.
我试图查看系统.IO.Pipelines 并使用 JSON API 的 ReadOnlySequence
重载,但它变得非常复杂.
I tried to look into System.IO.Pipelines and use ReadOnlySequence<byte>
overloads of JSON API, but it gets very complicated.
推荐答案
我认为文档需要更新,因为 .NET Core 3 有一个 直接从方法流.使用它很简单,假设流是用 UTF8 编码的:
I believe that documentation needs to be updated because .NET Core 3 has a method to read from a stream directly. Using it is straight-forward, assuming the stream is encoded in 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:
One thing to watch out for is that by default HttpClient will buffer the response content in-memory before returning unless you set the HttpCompletionOption to ResponseHeadersRead when invoking SendAsync:
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
这篇关于如何使用 System.Text.Json API 将流反序列化为对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!