Skip to content

[API Proposal]: Add PipeReader and PipeWriter overloads to JsonSerializer.SerializeAsync/DeserializeAsync #68586

@davidfowl

Description

@davidfowl

Background and motivation

When we originally did System.Text.Json there was some discussion about adding overloads to support PipeReader and PipeWriter. These discussion died because we didn't want to decide on forcing every API that had a Stream overload to have one for PipeReader/PipeWriter (some of that is here #28325). ASP.NET Core can save on allocations and excess buffer copies if we had support for these primitives in the JSON serializer. This likely isn't the only change we need to make to get great performance (there are some tweaks to pipelines that can be made to improve buffer sizes), but the idea is to add the APIs so that we can improve this on top.

Today in ASP.NET Core, we use the Stream overloads and:

  • On the Deserialize side, there's no need to further copy that into a buffer before parsing JSON.
  • On the Serialize side, we can avoid, the PooledBufferWriter allocation, and the extra buffer copies into the underlying Stream (which wrap the PipeWriter).

Of course we should measure and see how this materializes but we need to expose APIs to do so (at least privately so that ASP.NET Core can properly plumb them through the chain).

API Proposal

namespace System.Collections.Generic;

public static class JsonSerializer
{
    public static Task SerializeAsync<TValue>(
           PipeWriter utf8Json, 
           TValue value,  
           JsonTypeInfo<TValue> jsonTypeInfo, 
           CancellationToken cancellationToken = default);

    public static Task SerializeAsync<TValue>(
           PipeWriter utf8Json, 
           TValue value, 
           JsonSerializerOptions? options = null, 
           CancellationToken cancellationToken = default);
   
    public static Task SerializeAsync(
            PipeWriter utf8Json,
            object? value,
            JsonTypeInfo jsonTypeInfo,
            CancellationToken cancellationToken = default);
            
    public static Task SerializeAsync(
            PipeWriter utf8Json,
            object? value,
            Type inputType,
            JsonSerializerContext context,
            CancellationToken cancellationToken = default)
    
    public static ValueTask<TValue?> DeserializeAsync<TValue>(
            PipeReader utf8Json, 
            JsonSerializerOptions? options = null, 
            CancellationToken cancellationToken = default);
    
    public static ValueTask<TValue?> DeserializeAsync<TValue>(
            PipeReader utf8Json,
            JsonTypeInfo<TValue> jsonTypeInfo,
            CancellationToken cancellationToken = default);

    public static ValueTask<object?> DeserializeAsync(
            PipeReader utf8Json,
            JsonTypeInfo jsonTypeInfo,
            CancellationToken cancellationToken = default);
            
    public static ValueTask<object?> DeserializeAsync(
            PipeReader utf8Json,
            Type returnType,
            JsonSerializerContext context,
            CancellationToken cancellationToken = default);
}

API Usage

var pipe = new Pipe();
await JsonSerialize.SerilizeAsync(pipe.Writer, new Person { Name = "David" });
await pipe.Writer.CompleteAsync();
var result = await  JsonSerialize.DeserilizeAsync<Person>(pipe.Reader);
await pipe.Reader.CompleteAsync();

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.Text.Jsonin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions