【问题标题】:How to deserialize stream to object using System.Text.Json APIs如何使用 System.Text.Json API 将流反序列化为对象
【发布时间】: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&lt;byte&gt; 重载,但它变得非常复杂。

【问题讨论】:

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


    【解决方案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
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多