From 76dce87cb3e50331848b680067210fe4f57d86a0 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Mon, 8 Jan 2024 19:41:52 +0000 Subject: [PATCH] Fix fast-path support for ignoring nullable value types --- .../Common/src/SourceGenerators/TypeRef.cs | 2 +- .../tests/Common/PropertyVisibilityTests.cs | 77 +++---------------- .../Serialization/PropertyVisibilityTests.cs | 37 +++++++++ 3 files changed, 48 insertions(+), 68 deletions(-) diff --git a/src/libraries/Common/src/SourceGenerators/TypeRef.cs b/src/libraries/Common/src/SourceGenerators/TypeRef.cs index cfbf33ed741366..a4d556ef786dbe 100644 --- a/src/libraries/Common/src/SourceGenerators/TypeRef.cs +++ b/src/libraries/Common/src/SourceGenerators/TypeRef.cs @@ -19,7 +19,7 @@ public TypeRef(ITypeSymbol type) FullyQualifiedName = type.GetFullyQualifiedName(); IsValueType = type.IsValueType; TypeKind = type.TypeKind; - SpecialType = type.SpecialType; + SpecialType = type.OriginalDefinition.SpecialType; } public string Name { get; } diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs index 79a6ce599ad83c..44c702025f5d1e 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs @@ -1322,10 +1322,6 @@ public async Task JsonIgnoreAttribute() } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Needs support for more collections. - [ActiveIssue("https://github.com/dotnet/runtime/issues/53393")] -#endif public async Task JsonIgnoreAttribute_UnsupportedCollection() { string json = @@ -1771,10 +1767,6 @@ public async Task OverrideJsonIgnorePropertyUsingJsonPropertyNameReversed() [Theory] [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways))] [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways_Ctor))] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreConditionSetToAlwaysWorks(Type type) { string json = @"{""MyString"":""Random"",""MyDateTime"":""2020-03-23"",""MyInt"":4}"; @@ -1798,7 +1790,7 @@ public class ClassWithProperty_IgnoreConditionAlways public int MyInt { get; set; } } - private class ClassWithProperty_IgnoreConditionAlways_Ctor + public class ClassWithProperty_IgnoreConditionAlways_Ctor { public string MyString { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.Always)] @@ -1814,10 +1806,6 @@ public ClassWithProperty_IgnoreConditionAlways_Ctor(DateTime myDateTime, int myI [Theory] [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_ClassProperty_TestData))] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreConditionWhenWritingDefault_ClassProperty(Type type, JsonSerializerOptions options) { // Property shouldn't be ignored if it isn't null. @@ -1868,7 +1856,7 @@ public class ClassWithClassProperty_IgnoreConditionWhenWritingDefault public int Int2 { get; set; } } - private class ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor + public class ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor { public int Int1 { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] @@ -1892,10 +1880,6 @@ public static IEnumerable JsonIgnoreConditionWhenWritingDefault_ClassP [Theory] [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_StructProperty_TestData))] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreConditionWhenWritingDefault_StructProperty(Type type, JsonSerializerOptions options) { // Property shouldn't be ignored if it isn't null. @@ -1924,7 +1908,7 @@ public class ClassWithStructProperty_IgnoreConditionWhenWritingDefault public int Int2 { get; set; } } - private struct StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor + public struct StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor { public int Int1 { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] @@ -1948,10 +1932,6 @@ public static IEnumerable JsonIgnoreConditionWhenWritingDefault_Struct [Theory] [MemberData(nameof(JsonIgnoreConditionNever_TestData))] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreConditionNever(Type type) { // Property should always be (de)serialized, even when null. @@ -1983,10 +1963,6 @@ public async Task JsonIgnoreConditionNever(Type type) [Theory] [MemberData(nameof(JsonIgnoreConditionNever_TestData))] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreConditionNever_IgnoreNullValues_True(Type type) { // Property should always be (de)serialized. @@ -2060,9 +2036,6 @@ public async Task JsonIgnoreCondition_LastOneWins() } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - [ActiveIssue("https://github.com/dotnet/runtime/issues/53393")] -#endif public async Task ClassWithComplexObjectsUsingIgnoreWhenWritingDefaultAttribute() { string json = @"{""Class"":{""MyInt16"":18}, ""Dictionary"":null}"; @@ -2091,9 +2064,6 @@ public class ClassUsingIgnoreWhenWritingDefaultAttribute } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - [ActiveIssue("https://github.com/dotnet/runtime/issues/53393")] -#endif public async Task ClassWithComplexObjectUsingIgnoreNeverAttribute() { string json = @"{""Class"":null, ""Dictionary"":null}"; @@ -2375,10 +2345,6 @@ public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_Cl } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_LargeStructTest() { var options = new JsonSerializerOptions { IgnoreNullValues = true }; @@ -2412,10 +2378,6 @@ public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_La } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task ValueType_Properties_NotIgnoredWhen_IgnoreNullValues_Active_SmallStructTest() { var options = new JsonSerializerOptions { IgnoreNullValues = true }; @@ -2478,7 +2440,7 @@ public LargeStructWithValueAndReferenceTypes( } } - private struct SmallStructWithValueAndReferenceTypes + public struct SmallStructWithValueAndReferenceTypes { public string MyString { get; } public int MyInt { get; set; } @@ -2502,10 +2464,6 @@ public SmallStructWithValueAndReferenceTypes( public class PointClass { } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task Ignore_WhenWritingNull_Globally() { var options = new JsonSerializerOptions @@ -2579,17 +2537,8 @@ public class ClassWithThingsToIgnore } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Need support for parameterized ctors. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task Ignore_WhenWritingNull_PerProperty() { - var options = new JsonSerializerOptions - { - IncludeFields = true - }; - string json = @"{ ""MyPointClass2_IgnoredWhenWritingNull"":{}, ""MyString1_IgnoredWhenWritingNull"":""Default"", @@ -2604,7 +2553,7 @@ public async Task Ignore_WhenWritingNull_PerProperty() }"; // All members should correspond to JSON contents, as ignore doesn't apply to deserialization. - ClassWithThingsToIgnore_PerProperty obj = await Serializer.DeserializeWrapper(json, options); + ClassWithThingsToIgnore_PerProperty obj = await Serializer.DeserializeWrapper(json); Assert.NotNull(obj.MyPointClass2_IgnoredWhenWritingNull); Assert.Equal("Default", obj.MyString1_IgnoredWhenWritingNull); Assert.Null(obj.MyNullableBool1_IgnoredWhenWritingNull); @@ -2628,7 +2577,7 @@ public async Task Ignore_WhenWritingNull_PerProperty() ""MyNullableBool2_IgnoredWhenWritingNull"":true, ""MyPointStruct1"":{""X"":0,""Y"":0} }"; - JsonTestHelper.AssertJsonEqual(expectedJson, await Serializer.SerializeWrapper(obj, options)); + JsonTestHelper.AssertJsonEqual(expectedJson, await Serializer.SerializeWrapper(obj)); } public class ClassWithThingsToIgnore_PerProperty @@ -2639,6 +2588,7 @@ public class ClassWithThingsToIgnore_PerProperty [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string MyString2_IgnoredWhenWritingNull; + [JsonInclude] public int MyInt1; public int MyInt2 { get; set; } @@ -2646,15 +2596,16 @@ public class ClassWithThingsToIgnore_PerProperty [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? MyNullableBool1_IgnoredWhenWritingNull { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? MyNullableBool2_IgnoredWhenWritingNull; - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public PointClass MyPointClass1_IgnoredWhenWritingNull; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public PointClass MyPointClass2_IgnoredWhenWritingNull { get; set; } + [JsonInclude] public Point_2D_Struct_WithAttribute MyPointStruct1; public Point_2D_Struct_WithAttribute MyPointStruct2 { get; set; } @@ -2748,10 +2699,6 @@ public MyClassWithValueType() { } } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Needs bug fixes to custom converter handling. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreCondition_WhenWritingDefault_OnValueTypeWithCustomConverter() { var obj = new MyClassWithValueType(); @@ -2799,10 +2746,6 @@ public async Task JsonIgnoreCondition_ConverterCalledOnDeserialize() } [Fact] -#if BUILDING_SOURCE_GENERATOR_TESTS - // Needs bug fixes to custom converter handling. - [ActiveIssue("https://github.com/dotnet/runtime/issues/45448")] -#endif public async Task JsonIgnoreCondition_WhenWritingNull_OnValueTypeWithCustomConverter() { string json; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs index e614b9d938ed8c..89635a6b64b5f0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs @@ -334,6 +334,14 @@ public override async Task ClassWithIgnoredAndPrivateMembers_DoesNotIncludeIgnor [JsonSerializable(typeof(DictionaryWithPrivateKeyAndValueType))][JsonSerializable(typeof(ClassWithIgnoredAndPrivateMembers))] [JsonSerializable(typeof(ClassWithInternalJsonIncludeProperties))] [JsonSerializable(typeof(ClassWithIgnoredAndPrivateMembers))] + [JsonSerializable(typeof(ClassUsingIgnoreWhenWritingDefaultAttribute))] + [JsonSerializable(typeof(ClassUsingIgnoreNeverAttribute))] + [JsonSerializable(typeof(ClassWithIgnoredUnsupportedDictionary))] + [JsonSerializable(typeof(ClassWithProperty_IgnoreConditionAlways_Ctor))] + [JsonSerializable(typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor))] + [JsonSerializable(typeof(StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor))] + [JsonSerializable(typeof(SmallStructWithValueAndReferenceTypes))] + [JsonSerializable(typeof(WrapperForClassWithIgnoredUnsupportedDictionary))] [JsonSerializable(typeof(Class1))] [JsonSerializable(typeof(Class2))] [JsonSerializable(typeof(NamespaceBase.Class1), TypeInfoPropertyName = "Class1FromNamespaceBase")] @@ -427,6 +435,27 @@ public void PublicContextAndJsonSerializerOptions() Assert.Equal(obj.MaxDepth, deserialized.MaxDepth); } + [Fact] + public void PocoWithNullableProperties_IgnoresNullValuesWithGlobalSetting() + { + // Regression test for https://github.com/dotnet/runtime/issues/96404 + var value = new PocoWithNullableProperties(); + string json = JsonSerializer.Serialize(value, DefaultContextWithGlobalIgnoreSetting.Default.PocoWithNullableProperties); + Assert.Equal("{}", json); + } + + class PocoWithNullableProperties + { + public string? NullableRefType { get; set; } + public int? NullableValueType { get; set; } + } + + [JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(PocoWithNullableProperties))] + partial class DefaultContextWithGlobalIgnoreSetting : JsonSerializerContext; + [JsonSerializable(typeof(ClassWithNewSlotField))] [JsonSerializable(typeof(int))] [JsonSerializable(typeof(object))] @@ -578,6 +607,14 @@ public void PublicContextAndJsonSerializerOptions() [JsonSerializable(typeof(DictionaryWithPrivateKeyAndValueType))] [JsonSerializable(typeof(ClassWithInternalJsonIncludeProperties))] [JsonSerializable(typeof(ClassWithIgnoredAndPrivateMembers))] + [JsonSerializable(typeof(ClassUsingIgnoreWhenWritingDefaultAttribute))] + [JsonSerializable(typeof(ClassUsingIgnoreNeverAttribute))] + [JsonSerializable(typeof(ClassWithIgnoredUnsupportedDictionary))] + [JsonSerializable(typeof(ClassWithProperty_IgnoreConditionAlways_Ctor))] + [JsonSerializable(typeof(ClassWithClassProperty_IgnoreConditionWhenWritingDefault_Ctor))] + [JsonSerializable(typeof(StructWithStructProperty_IgnoreConditionWhenWritingDefault_Ctor))] + [JsonSerializable(typeof(SmallStructWithValueAndReferenceTypes))] + [JsonSerializable(typeof(WrapperForClassWithIgnoredUnsupportedDictionary))] [JsonSerializable(typeof(Class1))] [JsonSerializable(typeof(Class2))] [JsonSerializable(typeof(NamespaceBase.Class1), TypeInfoPropertyName = "Class1FromNamespaceBase")]