From 56483b81d057da8d23d46d161484f432f6dc0251 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Wed, 26 Jul 2023 08:55:32 +0400 Subject: [PATCH 1/8] fix(86031): Processing "RequiredMemberAttribute" by whole type hierarchy. --- .../DefaultJsonTypeInfoResolver.Helpers.cs | 24 +++++++++++++++---- .../tests/Common/RequiredKeywordTests.cs | 20 ++++++++++++++++ .../Serialization/RequiredKeywordTests.cs | 1 + 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs index ea8d86203e7236..ea6969008129d8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs @@ -76,11 +76,7 @@ private static void PopulateProperties(JsonTypeInfo typeInfo) Debug.Assert(!typeInfo.IsReadOnly); Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); - // Compiler adds RequiredMemberAttribute to type if any of the members is marked with 'required' keyword. - // SetsRequiredMembersAttribute means that all required members are assigned by constructor and therefore there is no enforcement - bool shouldCheckMembersForRequiredMemberAttribute = - typeInfo.Type.HasRequiredMemberAttribute() - && !(typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false); + bool shouldCheckMembersForRequiredMemberAttribute = ShouldCheckMembersForRequiredMemberAttribute(typeInfo); JsonTypeInfo.PropertyHierarchyResolutionState state = new(); @@ -408,5 +404,23 @@ internal static void DeterminePropertyAccessors(JsonPropertyInfo jsonPrope break; } } + + [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] + private static bool ShouldCheckMembersForRequiredMemberAttribute(JsonTypeInfo typeInfo) + { + // SetsRequiredMembersAttribute means that all required members are assigned by constructor and therefore there is no enforcement + if (!(typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false)) + { + foreach (Type currentType in typeInfo.Type.GetSortedTypeHierarchy()) + { + // Compiler adds RequiredMemberAttribute to type if any of the members is marked with 'required' keyword. + if (currentType.HasRequiredMemberAttribute()) + { + return true; + } + } + } + return false; + } } } diff --git a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs index f1163eef7aacc5..3fffc393f74892 100644 --- a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs @@ -143,6 +143,26 @@ public async Task ClassWithRequiredKeywordAndSmallParametrizedCtorFailsDeseriali Assert.Contains("Info2", exception.Message); } + [Fact] + public async Task InheritedPersonWithRequiredMembersWorksAsExpected() + { + var options = new JsonSerializerOptions(Serializer.DefaultOptions) { IncludeFields = true }; + options.MakeReadOnly(); + + JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembers)); + Assert.Equal(3, typeInfo.Properties.Count); + + AssertJsonTypeInfoHasRequiredProperties(GetTypeInfo(options), + nameof(InheritedPersonWithRequiredMembers.FirstName), + nameof(InheritedPersonWithRequiredMembers.LastName)); + + await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper("{}", typeInfo)); + } + + public class InheritedPersonWithRequiredMembers : PersonWithRequiredMembers + { + } + public class PersonWithRequiredMembersAndSmallParametrizedCtor { public required string FirstName { get; set; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs index 58cae747fef4c4..ee213eb44aa528 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs @@ -18,6 +18,7 @@ public RequiredKeywordTests_SourceGen() { } + [JsonSerializable(typeof(InheritedPersonWithRequiredMembers))] [JsonSerializable(typeof(PersonWithRequiredMembers))] [JsonSerializable(typeof(PersonWithRequiredMembersAndSmallParametrizedCtor))] [JsonSerializable(typeof(PersonWithRequiredMembersAndLargeParametrizedCtor))] From 1643205fabd55a5bcfef58b7feee90f04c83c27f Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 27 Jul 2023 08:28:43 +0400 Subject: [PATCH 2/8] fix(86031): Optimization of calculation for RequiredMemberAttribute checking requirements. --- .../DefaultJsonTypeInfoResolver.Helpers.cs | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs index ea6969008129d8..828ef82dc019e0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs @@ -76,7 +76,8 @@ private static void PopulateProperties(JsonTypeInfo typeInfo) Debug.Assert(!typeInfo.IsReadOnly); Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); - bool shouldCheckMembersForRequiredMemberAttribute = ShouldCheckMembersForRequiredMemberAttribute(typeInfo); + bool constructorHasSetsRequiredMembersAttribute + = typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false; JsonTypeInfo.PropertyHierarchyResolutionState state = new(); @@ -89,6 +90,9 @@ private static void PopulateProperties(JsonTypeInfo typeInfo) break; } + bool shouldCheckMembersForRequiredMemberAttribute + = !constructorHasSetsRequiredMembersAttribute && currentType.HasRequiredMemberAttribute(); + AddMembersDeclaredBySuperType( typeInfo, currentType, @@ -404,23 +408,5 @@ internal static void DeterminePropertyAccessors(JsonPropertyInfo jsonPrope break; } } - - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - private static bool ShouldCheckMembersForRequiredMemberAttribute(JsonTypeInfo typeInfo) - { - // SetsRequiredMembersAttribute means that all required members are assigned by constructor and therefore there is no enforcement - if (!(typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false)) - { - foreach (Type currentType in typeInfo.Type.GetSortedTypeHierarchy()) - { - // Compiler adds RequiredMemberAttribute to type if any of the members is marked with 'required' keyword. - if (currentType.HasRequiredMemberAttribute()) - { - return true; - } - } - } - return false; - } } } From 276c4fb35039c5ab8250b9295594c647a0f859cf Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 27 Jul 2023 10:35:37 +0400 Subject: [PATCH 3/8] feat(86031): Adding additional tests for inheritance with required modifiers. --- .../tests/Common/RequiredKeywordTests.cs | 81 ++++++++++++++++++- .../Serialization/RequiredKeywordTests.cs | 2 + ...m.Text.Json.SourceGeneration.Tests.targets | 4 + .../System.Text.Json.Tests.csproj | 3 + 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs index 3fffc393f74892..6f9443ec60cc6b 100644 --- a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; using System.Threading.Tasks; +using FluentAssertions; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -146,7 +147,7 @@ public async Task ClassWithRequiredKeywordAndSmallParametrizedCtorFailsDeseriali [Fact] public async Task InheritedPersonWithRequiredMembersWorksAsExpected() { - var options = new JsonSerializerOptions(Serializer.DefaultOptions) { IncludeFields = true }; + var options = new JsonSerializerOptions(Serializer.DefaultOptions); options.MakeReadOnly(); JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembers)); @@ -156,13 +157,72 @@ public async Task InheritedPersonWithRequiredMembersWorksAsExpected() nameof(InheritedPersonWithRequiredMembers.FirstName), nameof(InheritedPersonWithRequiredMembers.LastName)); - await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper("{}", typeInfo)); + JsonException exception = await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper("{}", typeInfo)); + Assert.Contains("FirstName", exception.Message); + Assert.Contains("LastName", exception.Message); + Assert.DoesNotContain("MiddleName", exception.Message); + } + + [Fact] + public async Task InheritedPersonWithRequiredMembersWithAdditionalRequiredMembersWorksAsExpected() + { + var options = new JsonSerializerOptions(Serializer.DefaultOptions); + options.MakeReadOnly(); + + JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers)); + Assert.Equal(4, typeInfo.Properties.Count); + + AssertJsonTypeInfoHasRequiredProperties(GetTypeInfo(options), + nameof(InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers.FirstName), + nameof(InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers.LastName), + nameof(InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers.Post)); + + JsonException exception = await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper("{}", typeInfo)); + Assert.Contains("FirstName", exception.Message); + Assert.Contains("LastName", exception.Message); + Assert.Contains("Post", exception.Message); + Assert.DoesNotContain("MiddleName", exception.Message); + } + + [Theory] + [MemberData(nameof(InheritedPersonWithRequiredMembersSetsRequiredMembersWorksAsExpectedSources))] + public async Task InheritedPersonWithRequiredMembersSetsRequiredMembersWorksAsExpected(string jsonValue, + InheritedPersonWithRequiredMembersSetsRequiredMembers expectedValue) + { + var options = new JsonSerializerOptions(Serializer.DefaultOptions); + options.MakeReadOnly(); + + JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembersSetsRequiredMembers)); + Assert.Equal(3, typeInfo.Properties.Count); + + AssertJsonTypeInfoHasRequiredProperties(GetTypeInfo(options)); + + InheritedPersonWithRequiredMembersSetsRequiredMembers actualValue = + await Serializer.DeserializeWrapper(jsonValue, options); + actualValue.Should() + .BeEquivalentTo(expectedValue); } public class InheritedPersonWithRequiredMembers : PersonWithRequiredMembers { } + public class InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers : PersonWithRequiredMembers + { + public required string Post { get; set; } + } + + public class InheritedPersonWithRequiredMembersSetsRequiredMembers : PersonWithRequiredMembers + { + [SetsRequiredMembers] + public InheritedPersonWithRequiredMembersSetsRequiredMembers() + { + FirstName = "FirstNameValueFromConstructor"; + LastName = "LastNameValueFromConstructor"; + MiddleName = "MiddleNameValueFromConstructor"; + } + } + public class PersonWithRequiredMembersAndSmallParametrizedCtor { public required string FirstName { get; set; } @@ -604,6 +664,23 @@ public class ClassWithCustomRequiredPropertyName public required int PropertyWithInitOnlySetter { get; init; } } + public static IEnumerable InheritedPersonWithRequiredMembersSetsRequiredMembersWorksAsExpectedSources() + { + yield return new object[] + { + "{}", + new InheritedPersonWithRequiredMembersSetsRequiredMembers() + }; + yield return new object[] + { + """{"FirstName": "FirstNameFromJson"}""", + new InheritedPersonWithRequiredMembersSetsRequiredMembers + { + FirstName = "FirstNameFromJson" + } + }; + } + private static JsonTypeInfo GetTypeInfo(JsonSerializerOptions options) { options.TypeInfoResolver ??= JsonSerializerOptions.Default.TypeInfoResolver; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs index ee213eb44aa528..91a51b77dc4b0c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/RequiredKeywordTests.cs @@ -19,6 +19,8 @@ public RequiredKeywordTests_SourceGen() } [JsonSerializable(typeof(InheritedPersonWithRequiredMembers))] + [JsonSerializable(typeof(InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers))] + [JsonSerializable(typeof(InheritedPersonWithRequiredMembersSetsRequiredMembers))] [JsonSerializable(typeof(PersonWithRequiredMembers))] [JsonSerializable(typeof(PersonWithRequiredMembersAndSmallParametrizedCtor))] [JsonSerializable(typeof(PersonWithRequiredMembersAndLargeParametrizedCtor))] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index b350fc936c7983..89af590ea05b93 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -22,6 +22,10 @@ $(DefineConstants);BUILDING_SOURCE_GENERATOR_TESTS + + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 2b79fd957e8d85..63d34a1277d0d2 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -25,6 +25,9 @@ + + + From 7068976415da53b5a90438599f9f0b71ca17a752 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 27 Jul 2023 14:36:35 +0400 Subject: [PATCH 4/8] fix(86031): Fix invalid number conversion for early existed tests. --- .../System.Text.Json/tests/Common/NumberHandlingTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs b/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs index 109123b92a0093..6b16a631576fa2 100644 --- a/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs @@ -104,7 +104,7 @@ private static string GetNumberAsString(T number) double @double => @double.ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture), float @float => @float.ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture), decimal @decimal => @decimal.ToString(CultureInfo.InvariantCulture), - _ => number.ToString() + _ => Convert.ToString(number, CultureInfo.InvariantCulture) }; } From 8d23440e014cf55563693a662e989837a5870ecf Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 27 Jul 2023 16:13:57 +0400 Subject: [PATCH 5/8] fix(86031): Fixes of formatting. --- .../Metadata/DefaultJsonTypeInfoResolver.Helpers.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs index 828ef82dc019e0..8ffa6a7ea1e339 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs @@ -76,8 +76,9 @@ private static void PopulateProperties(JsonTypeInfo typeInfo) Debug.Assert(!typeInfo.IsReadOnly); Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); - bool constructorHasSetsRequiredMembersAttribute - = typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false; + // SetsRequiredMembersAttribute means that all required members are assigned by constructor and therefore there is no enforcement + bool constructorHasSetsRequiredMembersAttribute = + typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false; JsonTypeInfo.PropertyHierarchyResolutionState state = new(); @@ -90,8 +91,9 @@ bool constructorHasSetsRequiredMembersAttribute break; } - bool shouldCheckMembersForRequiredMemberAttribute - = !constructorHasSetsRequiredMembersAttribute && currentType.HasRequiredMemberAttribute(); + // Compiler adds RequiredMemberAttribute to type if any of the members is marked with 'required' keyword. + bool shouldCheckMembersForRequiredMemberAttribute = + !constructorHasSetsRequiredMembersAttribute && currentType.HasRequiredMemberAttribute(); AddMembersDeclaredBySuperType( typeInfo, From aa502cc80911c3221c4c5e3ea25d093ab6bd9873 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 27 Jul 2023 16:19:48 +0400 Subject: [PATCH 6/8] fix(86031): Removing FluentAssertions dependency. --- .../System.Text.Json/tests/Common/RequiredKeywordTests.cs | 6 +++--- .../System.Text.Json.Tests/System.Text.Json.Tests.csproj | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs index 6f9443ec60cc6b..8741b76ce9df7d 100644 --- a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; using System.Threading.Tasks; -using FluentAssertions; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -199,8 +198,9 @@ public async Task InheritedPersonWithRequiredMembersSetsRequiredMembersWorksAsEx InheritedPersonWithRequiredMembersSetsRequiredMembers actualValue = await Serializer.DeserializeWrapper(jsonValue, options); - actualValue.Should() - .BeEquivalentTo(expectedValue); + Assert.Equal(expectedValue.FirstName, actualValue.FirstName); + Assert.Equal(expectedValue.LastName, actualValue.LastName); + Assert.Equal(expectedValue.MiddleName, actualValue.MiddleName); } public class InheritedPersonWithRequiredMembers : PersonWithRequiredMembers diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 63d34a1277d0d2..2b79fd957e8d85 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -25,9 +25,6 @@ - - - From 76140b17f277d98abc222443b90d1581d7cfd783 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 27 Jul 2023 13:26:28 +0100 Subject: [PATCH 7/8] Update src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets --- .../System.Text.Json.SourceGeneration.Tests.targets | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index 89af590ea05b93..b350fc936c7983 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -22,10 +22,6 @@ $(DefineConstants);BUILDING_SOURCE_GENERATOR_TESTS - - - - From d9c83e3a088a15d81908dd8594a62cba6e67c879 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 27 Jul 2023 13:27:41 +0100 Subject: [PATCH 8/8] Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs --- .../Metadata/DefaultJsonTypeInfoResolver.Helpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs index 8ffa6a7ea1e339..9a13d7a60de541 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs @@ -91,7 +91,7 @@ private static void PopulateProperties(JsonTypeInfo typeInfo) break; } - // Compiler adds RequiredMemberAttribute to type if any of the members is marked with 'required' keyword. + // Compiler adds RequiredMemberAttribute to type if any of the members are marked with 'required' keyword. bool shouldCheckMembersForRequiredMemberAttribute = !constructorHasSetsRequiredMembersAttribute && currentType.HasRequiredMemberAttribute();