Skip to content

Fix JSON schema nullability for readonly properties - #132271

Open
Youssef1313 wants to merge 10 commits into
mainfrom
dev/ygerges/jsonexporter-nullability
Open

Fix JSON schema nullability for readonly properties#132271
Youssef1313 wants to merge 10 commits into
mainfrom
dev/ygerges/jsonexporter-nullability

Conversation

@Youssef1313

Copy link
Copy Markdown
Member

This a simpler fix for #131602 that is specific in JsonSchemaExporter.

Long-term, I think we would need to fix the underlying values of IsGetNullable and IsSetNullable. So this is more of a workaround to take in RC1, and hence keeping the linked issue open.

Copilot AI lite review requested due to automatic review settings August 13, 2026 11:41
@azure-pipelines

Copy link
Copy Markdown
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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-text-json
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 IsGetNullable when a getter exists, and IsSetNullable when 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.

Comment thread src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs Outdated
Comment thread src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 13, 2026 11:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • IsNullableSchema now correctly ignores IsSetNullable when propertyInfo.Set is null, but the polymorphic path still computes parentPolymorphicTypeIsNonNullable using propertyInfo is { IsGetNullable: false, IsSetNullable: false } (around line ~134). For get-only/read-only members, IsSetNullable is typically true (WriteState == Unknown) even though there is no setter, which means derived schemas can still be made nullable via the !parentPolymorphicTypeIsNonNullable check.

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);
                        }

Copilot AI review requested due to automatic review settings August 14, 2026 04:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 IsNullableSchema still implies setter nullability is considered even when a property has no setter. Since the implementation now gates IsGetNullable/IsSetNullable on 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) ||

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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; }
}

@Youssef1313 Youssef1313 Aug 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched to an approach to prefer the nullability from AssociatedParameter.

@jeffhandley

Copy link
Copy Markdown
Member

@Youssef1313 FYI I don't expect @eiriktsarpalis will be able to review this for porting into RC1 so we will target a backport to RC2.

@eiriktsarpalis

Copy link
Copy Markdown
Member

This is fixing an old bug which therefore does not meet the bar for servicing in RC2. This should be targeting .NET 12 only.

@Youssef1313

Copy link
Copy Markdown
Member Author

This is fixing an old bug which therefore does not meet the bar for servicing in RC2. This should be targeting .NET 12 only.

I agree. I was hoping to get it in before RC1 snap but that didn't happen. It's fine to get it for .NET 12.

@eiriktsarpalis

Copy link
Copy Markdown
Member

Let's revisit reviewing this PR after RC2 work is completed. Feel free to ping me at that time if I forget.

Copilot AI review requested due to automatic review settings August 18, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs:452

  • The comment describing the property-level nullability rules is now out of date: the implementation also considers constructor-parameter nullability (AssociatedParameter), and only considers getter/setter nullability when that accessor exists. Updating the comment will help keep the logic maintainable and avoid future regressions.
                        if (propertyInfo is not null)
                        {
                            if (propertyInfo.Get is not null && propertyInfo.IsGetNullable)
                            {
                                return true;

src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs:86

  • This test asserts behavior for both get-only properties and readonly fields, but the method name only mentions properties. Renaming it makes the intent clearer (and also removes the trailing whitespace on the signature line).
        [Fact]
        public void GetOnlyProperties_DoNotUseSetterNullabilityInSchema() 

Copilot AI review requested due to automatic review settings August 18, 2026 15:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Copilot AI review requested due to automatic review settings August 18, 2026 16:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 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:450

  • The nullability rules comment is now slightly out of sync with the implementation: IsNullableSchema also considers propertyInfo.AssociatedParameter.IsNullable (and it only considers IsSetNullable when a setter exists). Updating the comment will prevent future readers from missing that behavior.
                        // 3. We have a schema for a reference type, unless we're explicitly treating null-oblivious types as non-nullable.

                        if (propertyInfo is not null)
                        {
                            if (propertyInfo.Get is not null && propertyInfo.IsGetNullable)

@Youssef1313 Youssef1313 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eiriktsarpalis This is green now and ready for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants