Skip to content

Conversation

@sander1095
Copy link

@sander1095 sander1095 commented Oct 1, 2025

This pull request adds support for serializing and deserializing IReadOnlySet<T> collections in System.Text.Json for .NET versions greater or equal to 5.0. The changes are carefully scoped to only apply when targeting compatible frameworks, and include updates to the source generator, converter infrastructure, and test coverage.

Support for IReadOnlySet collections:

  • Added a new converter, IReadOnlySetOfTConverter, to handle serialization and deserialization of IReadOnlySet<T> types, falling back to HashSet<T> for deserialization. (src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlySetOfTConverter.cs)
  • Updated the source generator and metadata services to recognize and emit code for IReadOnlySet<T>, including new APIs like CreateIReadOnlySetInfo and corresponding enum values and method mappings. (src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs, src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs, src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs, src/libraries/System.Text.Json/gen/Model/CollectionType.cs, src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs, src/libraries/System.Text.Json/ref/System.Text.Json.cs) [1] [2] [3] [4] [5] [6]
  • Integrated the new converter into the converter factory logic so that IReadOnlySet<T> types are automatically handled when encountered. (src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs)
  • Updated the project file to ensure the new converter is only compiled for supported target frameworks. (src/libraries/System.Text.Json/src/System.Text.Json.csproj)

Test coverage for IReadOnlySet:

  • Added comprehensive tests to verify serialization and deserialization of IReadOnlySet<T> and related scenarios, including inheritance and custom interfaces. (src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Generic.Read.cs) [1] [2] [3] [4]

Minor fixes:

  • Corrected a typo in an exception helper method name from ReturnsJsonConverterFactorty to ReturnsJsonConverterFactory. (src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs, src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs) [1] [2]
    Fixes System.Text.Json cannot deserialize to IReadOnlySet #91875

This is based on existing test cases for IReadOnlyList and ISet
I'm pretty sure this is not the right direction, but it helps me with being able to run tests against an initial working implementation I'll work on next.

I'll discuss the current #IF !NETFRAMEWORK code setup with the team when the PR is ready for a first review
This seems useful for code that deserializes to a type that deserializes to a Set type that implements both interfaces.
Copilot AI review requested due to automatic review settings October 1, 2025 20:35
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Oct 1, 2025
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

This PR adds support for IReadOnlySet<T> serialization/deserialization to System.Text.Json. The implementation creates a new converter that uses HashSet<T> as the concrete type for IReadOnlySet<T> interface deserialization.

  • Adds IReadOnlySetOfTConverter<TCollection, TElement> converter that deserializes JSON arrays to HashSet<T> instances
  • Integrates the new converter into the converter factory with appropriate conditional compilation
  • Adds comprehensive test coverage for both generic and object-typed IReadOnlySet<T> scenarios

Reviewed Changes

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

Show a summary per file
File Description
IReadOnlySetOfTConverter.cs New converter implementation for IReadOnlySet<T> using HashSet<T> as backing type
IEnumerableConverterFactory.cs Integrates IReadOnlySet<T> converter into the factory with conditional compilation
System.Text.Json.csproj Adds the new converter file to the project
ThrowHelper.Serialization.cs Fixes typo in method name from "Factorty" to "Factory"
JsonConverterFactory.cs Updates method call to use corrected spelling
Array.ReadTests.cs Adds test methods for IReadOnlySet<T> deserialization
ReferenceHandlerTests.cs Adds JsonSerializable attributes for IReadOnlySet<T> test classes
TestData.cs Includes IReadOnlySet<T> test cases in success test data
TestClasses.cs Adds test class definitions and minor formatting change to dictionary initialization

@sander1095 sander1095 marked this pull request as draft October 1, 2025 20:37
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.

This would also require updates to the source generator so IReadOnlySet is recognized and relevant code gets generated.

@sander1095
Copy link
Author

This would also require updates to the source generator so IReadOnlySet is recognized and relevant code gets generated.

@eiriktsarpalis
Any pointers to where I need to get started on this? A nudge to a file would be enough, I can check existing code patterns and call sites to hopefully figure the rest out.

@eiriktsarpalis
Copy link
Member

Sure, you can get started by looking at how ISet<T> is being handled today, presumably IReadOnlySet<T> would get handled identically:

else if ((actualTypeToConvert = type.GetCompatibleGenericBaseType(_knownSymbols.ISetOfTType)) != null)
{
collectionType = CollectionType.ISet;
valueType = actualTypeToConvert.TypeArguments[0];
}

Also exclude the entirety of IReadOnlySetOfTConverter by moving the #IF up.
…ck to csproj, which is what @huoyaoyuan (most likely) meant in his PR feedback.

As far as I can see, there isn't a modern .NET identifier. Therefore, a check for .NETCoreApp is not enough, because that also includes < NET 5.0.

Because of this, I also added a check that we are on .NET 5 or higher. Only then will the file be compiled.
@sander1095
Copy link
Author

@eiriktsarpalis I've made changes to the source generators. Can you provide a check to see if this was done to your satisfaction? Also: If the unit tests pass, can I then assume the source generator implementation is succesful? I am not really sure how to test source generators this in the repo itself, so any tips on that would be great (if succesful unit tests aren't enough).

Thanks for your time!

sander1095 and others added 11 commits October 9, 2025 19:13
…er manual testing), I believe that IReadOnlySet<T> should also not support it.

TODO: Tests still need to be modified/deleted because of this change.
These tests always fail, and for good reasons. IReadOnlySet does not support CanPopulate. I verified that this is expected behavior by trying to populate IReadOnlyList, which also does not support it
I am rather sure these tests shouldn't succeed anyway. when I compare them to IReadOnlyList, (some) of these tests actually assert an exception.

My goal currently is to get a successful test run, and the reviewers can tell me which tests should still be added.
…y types.

Therefore, this test does not belong there.
…ssages.

While ISetOfTConverter does not have this, IReadOnlyDictionaryOfTKeyTValueConverter *does*. I am not sure if there is any logic for when we do or do not include this check, but clearer exception messages seem like a plus to me.
A lot of these were part of mutable collection type tests.

Previously, I added test cases for readonly collection types, and it seems like that those succeed, so I do not believe I am lowering the code coverage here.
@sander1095 sander1095 marked this pull request as ready for review October 18, 2025 12:04
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

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

@sander1095
Copy link
Author

sander1095 commented Oct 18, 2025

Hi @eiriktsarpalis and @huoyaoyuan !

I've spent quite a bit of time on this PR in the last 2 weeks. I am fairly certain support for IReadOnlySet is solid.

When I create a little test project and run JsonSerializer.Deserialize<IReadOnlySet<int>>("[1]"), the code works!

I have added tests for more in-depth coverage, but there's still some tests (especially for source generation) that fail, and I can't really figure out why. I've tried to dive deeper into these, but when I attach a debugger to the tests, nothing happens, and I've tried on WSL, Dev Container and Visual Studio.

Anyway, I believe this PR is at the point where it can get a review by the team. My goal is to obtain the following:

  • Some help/guidance on WHY remaining tests are failing, and how I can start approaching a fix. If the team can spare a bit of time to run this PR locally and guide me through what's going wrong, I'd really appreciate it!
  • Feedback on the core parts of the PR (Especially IReadOnlySetOfTConverter).
  • Feedback on current test coverage, and potential missing tests, or tests I should not have deleted in the last batch of commits.
    • In the last commit batch I have removed several test cases because I believe they are not relevant for IReadOnlySet.

Final question:
If this PR approaches acceptability but not merge-ability, could a team member assign hacktoberfest-accepted label ;)?

Copy link
Member

@huoyaoyuan huoyaoyuan left a comment

Choose a reason for hiding this comment

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

It's prioritized to finalize .NET 10 work in this month. I'm not a member of the team and can only provide auxiliary review.

Comment on lines +1347 to +1352

// Only modern .NET (>= 5.0) supports IReadOnlySet<T>.
#if NET
public static System.Text.Json.Serialization.Metadata.JsonTypeInfo<TCollection> CreateIReadOnlySetInfo<TCollection, TElement>(System.Text.Json.JsonSerializerOptions options, System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues<TCollection> collectionInfo) where TCollection : System.Collections.Generic.IReadOnlySet<TElement> { throw null; }
#endif

Copy link
Member

Choose a reason for hiding this comment

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

Public API can't be introduced without going through API review process. We need to go through the review like #86442 and Eirik would be responsible for this.

Comment on lines +55 to +60
// Only modern .NET (>= 5.0) supports IReadOnlySet<T>.
#if NET
public INamedTypeSymbol? IReadOnlySetOfTType => GetOrResolveType(typeof(IReadOnlySet<>), ref _IReadOnlySetOfTType);
private Option<INamedTypeSymbol?> _IReadOnlySetOfTType;
#endif

Copy link
Member

Choose a reason for hiding this comment

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

It's incorrect to use #if on target framework in source generator code. The generator itself can run on a different framework (.NET Framework 4.8 when using Visual Studio) than the target framework. See how Memory<T> is handled.

yield return new object[] { typeof(IDerivedIList) };
yield return new object[] { typeof(IDerivedISetOfT<string>) };

// Only modern .NET (>= 5.0) supports IReadOnlySet<T>.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: redundant comment; other types that only available on higher frameworks aren't commented like this.

<Compile Include="$(CoreLibSharedDir)System\Marvin.cs" Link="Common\System\Marvin.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '5.0'))">
Copy link
Member

Choose a reason for hiding this comment

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

.NET 5~7 aren't directly targeted by System.Text.Json any more. Just add it into the .NETCoreApp group, together with Int128Converter etc.

Copy link
Author

@sander1095 sander1095 Oct 19, 2025

Choose a reason for hiding this comment

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

This would then include .NET Core, which did not have support for IReadOnlySet. The >= net 5.0 check is necessary for successful compilation

Copy link
Member

Choose a reason for hiding this comment

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

System.Text.Json doesn't target .NET Core 1.1 ~ 7.0 in current version. No compilation will be affected.

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

Labels

area-System.Text.Json community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.Text.Json cannot deserialize to IReadOnlySet

3 participants