Fix JSON schema nullability for readonly properties - #132271
Conversation
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
There was a problem hiding this comment.
Pull request overview
Adjusts System.Text.Json’s JsonSchemaExporter nullability inference so schema nullability reflects available accessors (getter/setter) rather than treating missing accessors as nullable, and adds regression coverage for get-only/readonly members.
Changes:
- Update schema nullability logic to only consult
IsGetNullablewhen a getter exists, andIsSetNullablewhen a setter exists. - Add a regression test covering get-only properties and readonly fields in schema generation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs | Refines nullability decision to ignore setter nullability when no setter delegate exists (and vice versa). |
| src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs | Adds regression test + new POCO to validate non-nullable schema output for get-only properties and readonly fields. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs:452
IsNullableSchemanow correctly ignoresIsSetNullablewhenpropertyInfo.Setis null, but the polymorphic path still computesparentPolymorphicTypeIsNonNullableusingpropertyInfo is { IsGetNullable: false, IsSetNullable: false }(around line ~134). For get-only/read-only members,IsSetNullableis typicallytrue(WriteState == Unknown) even though there is no setter, which means derived schemas can still be made nullable via the!parentPolymorphicTypeIsNonNullablecheck.
Consider updating the parentPolymorphicTypeIsNonNullable computation to use the same accessor-existence gating as this change (i.e., treat a missing setter as non-nullable rather than consulting IsSetNullable).
if (propertyInfo is not null)
{
return (propertyInfo.Get is not null && propertyInfo.IsGetNullable) ||
(propertyInfo.Set is not null && propertyInfo.IsSetNullable);
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs:451
- The comment above
IsNullableSchemastill implies setter nullability is considered even when a property has no setter. Since the implementation now gatesIsGetNullable/IsSetNullableon accessor presence, please update the comment to match the new semantics (this avoids confusion for get-only properties, which is the scenario this change fixes).
if (propertyInfo is not null)
{
return (propertyInfo.Get is not null && propertyInfo.IsGetNullable) ||
(propertyInfo.Set is not null && propertyInfo.IsSetNullable);
| if (propertyInfo is not null) | ||
| { | ||
| return propertyInfo.IsGetNullable || propertyInfo.IsSetNullable; | ||
| return (propertyInfo.Get is not null && propertyInfo.IsGetNullable) || |
There was a problem hiding this comment.
@eiriktsarpalis I'm thinking more. Is this whole idea even correct? A property isn't necessarily writeable only via a setter. It could be writeable via a constructor.
public class C
{
public C(string? s) => S = s ?? string.Empty;
// get-only property. Non-nullable.
// But a null is okay to get deserialized.
// It's not produced by serialization, though.
public string S { get; }
}
This a simpler fix for #131602 that is specific in JsonSchemaExporter.
Long-term, I think we would need to fix the underlying values of
IsGetNullableandIsSetNullable. So this is more of a workaround to take in RC1, and hence keeping the linked issue open.