Skip to content

Provide an API for configuring JsonSerializerOptions instances for reflection-based serialization. #89934

@eiriktsarpalis

Description

@eiriktsarpalis

Background & Motivation

The System.Text.Json reflection-based serializer makes it possible for users to supply JsonSerializerOptions instances that don't have a TypeInfoResolver property explicitly configured. It does this by silently populating the TypeInfoResolver property with a reflection-based DefaultJsonTypeInfoResolver value and then locking the instance for further modification.

Using publicly available APIs, this semantic can be emulated as follows:

void ConfigureForReflection(JsonSerializerOptions options)
{
    options.TypeInfoResolver ??= new DefaultJsonTypeInfoResolver();
    options.MakeReadOnly();
}

The problem with this approach is that it isn't thread safe when applied to the same options instance. I encountered this issue when attempting to implement reflection-based initialization in System.Net.Http.Json (cf. #89830), which can only access public STJ APIs. At the moment, the only viable workaround for libraries like System.Net.Http.Json is to do the following:

if (options.TypeInfoResolver is null)
{
    // Run a basic serialization operation to force thread-safe initialization of the options instance.
    JsonSerializer.Deserialize<int>("0"u8, options);
}

While the above works, it is kind of wasteful in that it forces an unnecessary serialization operation. We need access to the API that simply primes the options instance for reflection-based usage.

API Proposal

Add a method to JsonSerializerOptions for configuring using reflection-based defaults:

namespace System.Text.Json;

public partial class JsonSerializerOptions
{
    // Throws IOE if TypeInfoResolver is null
    public void MakeReadOnly();

+   // Populates TypeInfoResolver with DefaultJsonTypeInfoResolver if null
+   // (or the empty resolver if the IsReflectionEnabledDefault switch is disabled)
+   [RequiresUnreferencedCode, RequiresDynamicCode]
+   public void MakeReadOnlyWithReflectionDefaults();
}

cc @eerhardt

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions