Skip to content

Add a STJ feature flag treating non-optional constructor parameters as required #100075

Closed
@eiriktsarpalis

Description

@eiriktsarpalis

Background and motivation

System.Text.Json constructor-based deserialization currently treats all constructor parameters as optional, as can be highlighted in the following example:

var result = JsonSerializer.Deserialize<Person>("{}");
Console.WriteLine(result); // Person { Name = , Age = 0 }

public record Person(string Name, int Age);

This can create unexpected validation problems since users typically think of non-optional parameters as required values in the object contract. It typically also results in deserialized properties violating their own nullability annotations. Another problem is that it could result either in discrepancies or overly verbose outputs in components that are attempting to derive a JSON schema from the STJ contracts.

Changing this behavior would most likely constitute a serious breaking change, so we would instead like to expose a feature flag that opts in to new behavior, marking non-optional constructor parameters as required. Rather than making this an application-wide feature switch, this should instead be a per-JsonSerializerOptions setting to avoid breaking third-party components making use of STJ.

Related to #1256

API Proposal

namespace System.Text.Json;

public partial class JsonSerializerOptions
{
    public bool RequireConstructorParameters { get; set; } = false;
}

namespace System.Text.Json.Serialization;

public partial class JsonSourceGenerationOptionsAttribute
{
    public bool RequireConstructorParameters { get; set; } = false;
}

API Usage

var options = new JsonSerializerOptions {  RequireNonOptionalConstructorParameters = true };

JsonSerializer.Deserialize<Person>("""{"Name" : "John", "Age" : 27 }""", options); // Success
JsonSerializer.Deserialize<Person>("""{"Name" : "John", "Address" : "known"}""", options);
//  JSON deserialization for type 'Person' was missing required properties, including the following: Age

public record Person(string Name, int Age, string Address = "unknown");

Alternative Designs

This proposal is very closely related to #100144. We should consider a design that unifies both behaviors under a single feature switch.

Risks

No response

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions