我正在从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并使用ReadOnlySequence<byte>
的JSON API重载,但这变得非常复杂。
答案 0 :(得分:2)
我认为文档需要更新,因为.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;
}
要注意的一件事是,默认情况下,除非在调用SendAsync时将HttpCompletionOption设置为ResponseHeadersRead,否则HttpClient将在返回之前在内存中缓冲响应内容:
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);