如何使用System.Text.Json API将流反序列化到对象

时间:2019-10-22 21:13:51

标签: c# json .net-core system.text.json

我正在从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重载,但这变得非常复杂。

1 个答案:

答案 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);