-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Background and motivation
The JsonSerializer class exposes deserialization overloads that accept JsonTypeInfo<T> and JsonTypeInfo. These overloads are generally marked AOT/linker-safe and provide the only entrypoint for serializing using materialized metadata instances. We are however missing non-generic overloads for doing the same thing for DeserializeAsyncEnumerable.
In my case, firstly we call an Rest API that respond an object schema. Then call another Rest API to get data. In this moment, we receive a response as stream and we use ExpandoObject class to deserialize that response. In my study, using a typed deserialization we have an improvement in memory usage compared to the ExpandoObject. Considering that the object to be deserialized is dynamic, we are creating a type using the System.Reflection.Emit.AssemblyBuilder API using the object schema recovered by first call and try to use DeserializeAsyncEnumerable.
If we use DeserializeAsync we will have to wait for the entire body response complete to interact with data received.
API Proposal
namespace System.Text.Json;
public partial static class JsonSerializer
{
public static IAsyncEnumerable<object?> DeserializeAsyncEnumerable(Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default);
public static IAsyncEnumerable<object?> DeserializeAsyncEnumerable(Stream utf8Json, JsonTypeInfo jsonTypeInfo, bool topLevelValues, CancellationToken cancellationToken = default);
public static IAsyncEnumerable<object?> DeserializeAsyncEnumerable(Stream utf8Json, Type returnType, JsonSerializerOptions? options = default, CancellationToken cancellationToken = default);
public static IAsyncEnumerable<object?> DeserializeAsyncEnumerable(Stream utf8Json, Type returnType, bool topLevelValues, JsonSerializerOptions? options = default, CancellationToken cancellationToken = default);
}API Usage
// create any dynamic type using System.Reflection.Emit.AssemblyBuilder
Type? returnType = tb.CreateType();
var json = """
[
{"Number": 1},
{"Number": 2},
{"Number": 3}
]
""";
byte[] bytes = Encoding.UTF8.GetBytes(json);
var stream = new MemoryStream(bytes);
var result = JsonSerializer.DeserializeAsyncEnumerable(stream, returnType);
await foreach (var item in result)
{
var property = returnType.GetProperty("Number");
var value = property.GetValue(item, null);
Console.WriteLine(value); // 1, 2, 3
}Alternative Designs
public static IAsyncEnumerable<object> DeserializeAsyncEnumerable(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
{
var serializer = typeof(JsonSerializer);
var deserializeAsyncEnumerable = serializer
.GetMethod("DeserializeAsyncEnumerable", BindingFlags.Public | BindingFlags.Static);
var typedDeserializeAsyncEnumerable = deserializeAsyncEnumerable.MakeGenericMethod(returnType);
var result = typedDeserializeAsyncEnumerable.Invoke(null, new object[] { utf8Json, options, cancellationToken });
return result as IAsyncEnumerable<object>;
}Risks
No response