Skip to content

Make the return type of JsonSerializerOptions.GetTypeInfo nullable. #81269

@eiriktsarpalis

Description

@eiriktsarpalis

Background and motivation

The STJ source generator's implementation of the JsonSerializerContext.GetTypeInfo and IJsonTypeInfoResolver.GetTypeInfo methods are currently duplicated:

public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(global::System.Type type)
{
    if (type == typeof(global::TestModel))
    {
        return this.TestModel;
    }

    if (type == typeof(global::System.Int32))
    {
        return this.Int32;
    }

    return null;
}

global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? global::System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver.GetTypeInfo(global::System.Type type, global::System.Text.Json.JsonSerializerOptions options)
{
    if (type == typeof(global::TestModel))
    {
        return Create_TestModel(options, makeReadOnly: false);
    }

    if (type == typeof(global::System.Int32))
    {
        return Create_Int32(options, makeReadOnly: false);
    }

    return null;
}

The size of these methods increases with the number of generated types, so in the interest of size reduction we would at least want to eliminate the duplication. The first GetTypeInfo method could be implemented by replacing it with

public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(global::System.Type type)
    => Options.GetTypeInfo(type);

There is a however a snag, since JsonSerializerContext.GetTypeInfo needs to return null for unsupported types, whereas JsonSerializerOptions.GetTypeInfo is non-nullable and returns NotSupportedException for unsupported types.

API Proposal

namespace System.Text.Json;

public partial class JsonSerializerOptions
{
    public JsonTypeInfo GetTypeInfo(Type type);
+    public bool TryGetTypeInfo(Type type, [NotNullWhen(true)] out JsonTypeInfo? typeInfo);
}

API Usage

The source generator could then implement the first GetTypeInfo as follows:

public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(global::System.Type type)
{
    Options.TryGetTypeInfo(type, out JsonTypeInfo? result);
    return result;
}

Testing against our own source gen test projects, the change contributes to ~2.2% size reduction to generated source files and just over 1.2% to the test assemblies (which includes test code beyond just the source generated contexts). A minor yet measurable improvement.

Alternative Designs

  1. Do not ship the a new API and instead source generate

    public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(global::System.Type type)
    {
        try
        {
            return Options.GetTypeInfo(type);
        }
        catch (NotSupportedException)
        {
             return null;
        }
    }
  2. Make the existing GetTypeInfo method nullable:

     public partial class JsonSerializerOptions
    {
    -        public JsonTypeInfo GetTypeInfo(Type type);
    +        public JsonTypeInfo? GetTypeInfo(Type type);
    }   

    It would return null where the method currently throws, so technically this is not a breaking change but it would trigger nullability warnings in places where it previous didn't apply.

Risks

No response

Metadata

Metadata

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.Text.Jsonsize-reductionIssues impacting final app size primary for size sensitive workloadssource-generatorIndicates an issue with a source generator feature

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions