-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a STJ feature flag recognizing nullable reference type annotations on properties #100144
Comments
Those |
Just bikeshedding here but the originally proposed |
That is my expectation. |
Sorry, it's just the comments for the properties indicate the opposite. |
Ah right, the comments predate the most recent naming change. Updated. |
You mean using "non-nullable" instead of "nullable" in the name? Even though "non-nullable reference types" is technically more precise vis-a-vis the language semantics when the feature is disabled, calling it "nullable reference types" isn't wrong either and in practice the two terms are used interchangeably. The latter tends to win because it's shorter. These names are far from final, I'm sure they will be adequately bikeshed during API review. |
I think using "non-nullable" wording limits the scope, "nullable reference types" implies that all suffixes are honored. It also guards against suffixes that could be added in the future. I'm also not sold on using "Resolve". If only |
Any possibility of specifying parsing behaviour for a missing Optionally, of course. This allows for "default" behaviour when null is not expected without having to declare all properties beforehand. Useful for settings files whose properties could change in the future. |
The comments still say it defaults to |
No, that's out of scope for this feature. It specifically controls what happens when a given property exists in the JSON and is deserializing to |
The scope of the feature is to accurately reflect all possible nullability states that can exist on a property (as modelled in, say, the Perhaps some confusion might stem from the fact that this feature can only get nullability info from properties and constructor parameters, and is not meant to be able to fully reason about NRT's (type-level or generic parameter annotations, or suppressions appended to a JsonSerializer callsite). If that's the case we could try to disambiguate by calling it something something more specific like |
And I just remembered that this scenario should already be covered by providing default values for the type you're deserializing, right? |
It's less the non, more the resolve. To me resolving means something like a name table lookup or what It could also be an enum e.g. |
Talking about nullability, is there any idea to deal with top-level nulls more fluently? I know that json literal |
It's something we could consider adding as a setting or attribute annotation on individual types, but it would be independent of nullable reference type annotations (IOW, it's not possible to distinguish between |
Following this as I've opened up another discussion where this issue was mentioned (marked answer from @huoyaoyuan) |
namespace System.Text.Json;
public partial class JsonSerializerOptions
{
public bool IgnoreNullableAnnotations { get; set; } = false;
}
public partial class JsonSourceGenerationOptionsAttribute
{
public bool IgnoreNullableAnnotations { get; set; } = false;
}
namespace System.Text.Json.Serialization.Metadata;
public partial class JsonPropertyInfo
{
public bool DisallowNullWrites { get; set; }
public bool DisallowNullReads { get; set; }
} |
Added
Tagging @dotnet/compat for awareness of the breaking change. |
Given the following scenario: var person1 = JsonSerializer.Deserialize<Person>("{Age: 25}");
var person2 = JsonSerializer.Deserialize<Person>("{FirstName: null, LastName: null}");
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
} If I understood correctly, this will result in the following:
Which, to me, does not feel like a "pit of success". I think You could argue that "Well, your type annotations were wrong" - and I would agree - but this proposal does not change anything in that regard; There are two main things you can do to fix this;
Both of these works just as well today as it will after this proposal, so I fail to see what we gain from this. Assuming I understood incorrectly - and what this proposal.. proposes.. is to mark NRTs as required - then I think that should be reflected in the names. Something like |
In this case person2 would still throw, person1 would be Lang team says nullability and requiredness are orthogonal concerns. OpenAPI Schema does the same (see https://swagger.io/docs/specification/data-models/data-types/ sections Null and Required Properties). STJ should follow suit. |
Having worked on the implementation (via #102499), me and @jozkee have come to the conclusion that making this behavior the default is going to create substantial disruption (the large number of failing unit tests providing a strong signal that this is going to be the case). We have decided to instead make this an opt-in feature, which correspondingly should influence a change to the APIs: public partial class JsonSerializerOptions
{
public class bool RespectRequiredConstructorParameters { get; set; } = false;
- public class IgnoreNullableAnnotations { get; set; } = true;
+ public class RespectNullableAnnotations { get; set; } = false;
}
public partial class JsonPropertyInfo
{
public Func<object, object?> Get { get; set; }
- public bool DisallowNullWrites { get; set; }
+ public bool IsGetNonNullable { get; set; }
public Action<object, object?> Set { get; set; }
- public bool DisallowNullReads { get; set; }
+ public bool IsSetNonNullable { get; set; }
}
public partial class JsonParameterInfoValues
{
- public bool DisallowNullReads { get; init; }
+ public bool IsNonNullable { get; init; }
} The name change from |
Thank you, this looks promising! I assume the middle ground of source generator looking at nullability settings in the project was not an option? e.g. having |
It wouldn't completely avoid the potential for breaking changes, nullable warnings are far from perfect and they can always be suppressed inside a codebase. |
namespace System.Text.Json;
public partial class JsonSerializerOptions
{
// Existing
// public class bool RespectRequiredConstructorParameters { get; set; } = false;
public class RespectNullableAnnotations { get; set; } = false;
}
public partial class JsonPropertyInfo
{
// Existing
// public Func<object, object?> Get { get; set; }
public bool IsGetNullable { get; set; }
// Existing
// public Action<object, object?> Set { get; set; }
public bool IsSetNullable { get; set; }
}
public partial class JsonParameterInfoValues
{
public bool IsNullable { get; init; }
} |
Background and motivation
STJ should be able to recognize non-nullable reference types in property and constructor parameter annotations and fail serialization or deserialization if the run-time values are found to violate nullability. Recognizing annotations is valuable from the perspective of JSON schema extraction, since the current nullable-oblivious semantics result in more permissive schemas than what users might desire.
Enabling this would be a breaking change, so it needs to be turned on via an opt-in flag. The proposal intentionally makes this a property rather than a process-wide feature switch so that individual components can use the semantics appropriate to them.
API Proposal
API usage
Alternative designs
This proposal is very closely related to #100075. We should consider a design that unifies both behaviors under a single feature switch.
Risks
Because of restrictions on how NRT's are implemented and the architecture used by JsonSerializer (contracts keyed on
System.Type
which is nullable-agnostic), it is currently not possible to determine nullability annotations for root-level types, generic properties or collection elements. For example, it is not possible to distinguish betweenPerson
andPerson?
orList<Person>
andList<Person?>
as root-level values. This could lead to user confusion so we should try to document this concern as clearly as possible.The text was updated successfully, but these errors were encountered: