Skip to content

Support type-level JsonIgnoreCondition via [JsonIgnore] on classes, structs, and interfaces#124646

Merged
eiriktsarpalis merged 9 commits intomainfrom
copilot/add-jsonignorecondition-type-level
Mar 19, 2026
Merged

Support type-level JsonIgnoreCondition via [JsonIgnore] on classes, structs, and interfaces#124646
eiriktsarpalis merged 9 commits intomainfrom
copilot/add-jsonignorecondition-type-level

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

Description

[JsonIgnore(Condition = ...)] can now be applied at the type level to set a default JsonIgnoreCondition for all properties and fields, eliminating per-property duplication:

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
class MyPoco
{
    public string? PropA { get; set; }
    public string? PropB { get; set; }
    public string? PropC { get; set; }
}

Property-level [JsonIgnore] overrides the type-level setting. The type-level annotation also overrides the global JsonSerializerOptions.DefaultIgnoreCondition — for example, a type-level WhenWritingNull takes precedence over a global WhenWritingDefault. For non-nullable value types where WhenWritingNull is inapplicable, the ignore condition is set to Never to ensure the type-level annotation still overrides the global JSO policy. JsonIgnoreCondition.Always is rejected at the type level as invalid configuration — the reflection path throws InvalidOperationException, and the source generator emits SYSLIB1226 warning diagnostic and ignores the attribute.

Changes

  • API surface: Expanded JsonIgnoreAttribute AttributeUsage to include Class | Struct | Interface, with API compat suppressions for the AttributeUsage change in both the shared framework baseline (ApiCompatBaseline.NetCoreAppLatestStable.xml) and the package-level baseline (CompatibilitySuppressions.xml for net10.0, net462, netstandard2.0)
  • Reflection resolver (DefaultJsonTypeInfoResolver.Helpers.cs): Resolves type-level [JsonIgnore] once per type in PopulateProperties and passes the resolved JsonIgnoreCondition? parametrically through AddMembersDeclaredBySuperTypeAddMemberCreatePropertyInfo, avoiding redundant per-member attribute lookups. Uses typeInfo.Type (root serialized type) so type-level attributes on derived classes apply to inherited base-class properties. Throws InvalidOperationException for JsonIgnoreCondition.Always at the type level. For WhenWritingNull on non-nullable value types, sets the ignore condition to Never to prevent the global DefaultIgnoreCondition from taking over.
  • Source generator (JsonSourceGenerator.Parser.cs): Reads type-level [JsonIgnore] in ProcessTypeCustomAttributes using cached JsonIgnoreAttributeType symbol from KnownTypeSymbols.cs, threads it through to property parsing as a fallback. Emits SYSLIB1226 diagnostic for JsonIgnoreCondition.Always at the type level. Applies the same WhenWritingNullNever mapping for non-nullable value types.
  • Diagnostic: Added SYSLIB1226 diagnostic descriptor, resource strings (including XLF translations), and updated list-of-diagnostics.md
  • Tests: Covers WhenWritingNull, WhenWritingDefault, Always (invalid config), property-level override, struct, inheritance, deserialization, and type-level annotation overriding global JsonSerializerOptions.DefaultIgnoreCondition scenarios across both reflection and source gen paths. Includes source generator unit test for the SYSLIB1226 diagnostic.
Original prompt

This section details on the original issue you should resolve

<issue_title>It should be possible to specify JsonIgnoreCondition's on the type level</issue_title>
<issue_description>## Motivation

There's a substantial amount of duplication in the following code:

class MyPoco
{
    [JsonIgnore(JsonIgnoreCondition.WhenWritingNull)]
    public string? PropA { get; set; }

    [JsonIgnore(JsonIgnoreCondition.WhenWritingNull)]
    public string? PropB { get; set; }

    [JsonIgnore(JsonIgnoreCondition.WhenWritingNull)]
    public string? PropC { get; set; }
}

that could be eliminated using a type-level declaration:

[JsonIgnore(JsonIgnoreCondition.WhenWritingNull)]
class MyPoco
{
    public string? PropA { get; set; }

    public string? PropB { get; set; }

    public string? PropC { get; set; }
}

API Proposal

namespace System.Text.Json.Serialization;

-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public partial class JsonIgnoreAttribute;

cc @stephentoub</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@dotnet-policy-service
Copy link
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 and others added 3 commits February 20, 2026 15:14
…n resolver, source generator)

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
…on value types

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
…dd inheritance and deserialization tests

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Copilot AI changed the title [WIP] Add JsonIgnoreCondition support at type level Support type-level JsonIgnoreCondition via [JsonIgnore] on classes, structs, and interfaces Feb 20, 2026
Copilot AI requested a review from eiriktsarpalis February 20, 2026 15:36
Copy link
Member

@eiriktsarpalis eiriktsarpalis left a comment

Choose a reason for hiding this comment

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

…YSLIB1226 diagnostic

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
…tion

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 24, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh pr checks 114212 --json name,state (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh pr checks 114212 --repo dotnet/runtime (http block)
    • Triggering command: /usr/bin/gh gh pr view --json number,url,headRefName (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@eiriktsarpalis
Copy link
Member

@copilot Please rebase changes and fix merge conflicts.

@eiriktsarpalis
Copy link
Member

@copilot Please rebase changes and fix merge conflicts.

Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot Please rebase changes and fix merge conflicts.

Merged latest main and resolved the conflict in JsonSourceGeneratorDiagnosticsTests.cs (kept both our JsonIgnoreConditionAlwaysOnTypeWarns test and main's new Diagnostic_HasPragmaSuppressibleLocation test). Commit 8af167a.

Copy link
Member

@eiriktsarpalis eiriktsarpalis left a comment

Choose a reason for hiding this comment

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

@copilot add testing showing that the attribute annotation overrides the global JSO setting.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for applying [JsonIgnore(Condition = ...)] at the type level to provide a default JsonIgnoreCondition for all members, with consistent behavior across reflection-based metadata and the System.Text.Json source generator.

Changes:

  • Expanded JsonIgnoreAttribute to allow usage on class, struct, and interface, with API compat/package validation suppressions.
  • Implemented type-level JsonIgnoreCondition fallback in both reflection resolver and source generator, including invalid-configuration handling for Always.
  • Added new source-generator warning SYSLIB1226 and comprehensive tests for the new behavior.

Reviewed changes

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

Show a summary per file
File Description
src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml Suppresses API compat diagnostic for the AttributeUsage change.
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonIgnoreAttribute.cs Extends attribute targets and updates XML docs to describe type-level behavior.
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs Adds reflection-path fallback to type-level ignore condition and rejects Always.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs Adds generator support for type-level ignore condition fallback + emits SYSLIB1226 for invalid Always.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs Introduces diagnostic descriptor for SYSLIB1226.
src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs Adds cached symbol lookup for JsonIgnoreAttribute.
src/libraries/System.Text.Json/gen/Resources/Strings.resx Adds resource strings for SYSLIB1226 title/message.
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.*.xlf Adds localization entries for the new diagnostic strings.
docs/project/list-of-diagnostics.md Documents SYSLIB1226 meaning in the diagnostics list.
src/libraries/System.Text.Json/ref/System.Text.Json.cs Updates reference assembly metadata for new attribute usage targets.
src/libraries/System.Text.Json/src/CompatibilitySuppressions.xml Adds package validation baseline suppressions for the AttributeUsage change across target TFMs.
src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs Adds reflection-path tests for type-level ignore semantics, overrides, structs, and inheritance.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs Adds source-gen-path test ensuring type-level Always is ignored (serialized normally).
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets Suppresses SYSLIB1226 in source-gen integration tests (covered explicitly elsewhere).
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs Adds compilation helper for SYSLIB1226 unit-test scenario.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs Adds unit test validating SYSLIB1226 warning and location.

You can also share your feedback on Copilot code review. Take the survey.

…faultIgnoreCondition

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot add testing showing that the attribute annotation overrides the global JSO setting.

Added JsonIgnoreCondition_TypeLevel_OverridesGlobalJSO test that verifies the type-level [JsonIgnore(Condition = WhenWritingNull)] overrides the global JsonSerializerOptions.DefaultIgnoreCondition = WhenWritingDefault. Also fixed a bug in both the reflection resolver and source generator where WhenWritingNull on non-nullable value types was leaving ignoreCondition as null, allowing the global JSO policy to take over. Now it's set to Never in that case, ensuring the type-level annotation always takes precedence. Commit a886e49.

Copy link
Member

@tarekgh tarekgh left a comment

Choose a reason for hiding this comment

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

LGTM. I added one comment on issue originally raised by copilot if you want to consider it.

… per-property methods

Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
@eiriktsarpalis
Copy link
Member

/ba-g unrelated mono build failures.

@eiriktsarpalis eiriktsarpalis merged commit c88b2b6 into main Mar 19, 2026
70 of 89 checks passed
@eiriktsarpalis eiriktsarpalis deleted the copilot/add-jsonignorecondition-type-level branch March 19, 2026 12:49
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.

It should be possible to specify JsonIgnoreCondition's on the type level

4 participants