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

qlvxas9a  于 2023-03-31  发布在  其他
关注(0)|答案(2)|浏览(151)

我从一个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 repo中找到了这个porting guide,其中section阅读from a Stream/String声明:
我们目前(从.NET Core 3.0预览版2开始)没有一个方便的API直接从流中读取JSON(同步或异步)。对于同步阅读(特别是小负载),您可以将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是一个long,我将其转换为int,这可能导致OverflowException
我试着研究System.IO.Pipelines并使用ReadOnlySequence<byte>重载JSON API,但它变得非常复杂。

toe95027

toe950271#

我认为文档需要更新,因为.NET Core 3有一个直接从流中读取的方法。使用它是直接的,假设流是用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);
jm81lzqq

jm81lzqq2#

从ASP.NET Core 6开始,您应该使用 DeserializeAsyncEnumerable() 方法:

using var request = new HttpRequestMessage(HttpMethod.Get, "/api/products");
request.SetBrowserResponseStreamingEnabled(true); // Enable response streaming
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
IAsyncEnumerable<Product?> products = JsonSerializer.DeserializeAsyncEnumerable<Product>(stream, new JsonSerializerOptions
    {
       PropertyNameCaseInsensitive = true,
       DefaultBufferSize = 128
     });

 await foreach (Product? product in products)
 {
   // ...
 }
}

相关问题