From ca4f51f29b90c3cd3b6e7db8a9922cb97ed0075d Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 9 Mar 2023 11:44:58 +0000 Subject: [PATCH] Fix handling of required & init-only properties with custom property names in source gen. (#83147) * Fix handling of required & init-only properties with custom property names in source gen. * Address feedback. --- .../gen/JsonSourceGenerator.Emitter.cs | 2 +- .../DefaultJsonTypeInfoResolver.Helpers.cs | 2 +- .../Common/PropertyVisibilityTests.InitOnly.cs | 27 ++++++++++++++++++++++ .../tests/Common/RequiredKeywordTests.cs | 22 ++++++++++++++++++ .../Serialization/PropertyVisibilityTests.cs | 4 ++++ .../Serialization/RequiredKeywordTests.cs | 1 + 6 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index c794436..c773f7d 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -830,7 +830,7 @@ private static {JsonParameterInfoValuesTypeRef}[] {typeGenerationSpec.TypeInfoPr sb.Append(@$" {InfoVarName} = new() {{ - Name = ""{spec.Property.JsonPropertyName ?? spec.Property.ClrName}"", + Name = ""{spec.Property.ClrName}"", ParameterType = typeof({spec.Property.TypeGenerationSpec.TypeRef}), Position = {spec.ParameterIndex}, HasDefaultValue = false, 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 6c48b6a..2d746e0 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 @@ -234,7 +234,7 @@ namespace System.Text.Json.Serialization.Metadata JsonConverter? customConverter; try { - customConverter = DefaultJsonTypeInfoResolver.GetCustomConverterForMember(typeToConvert, memberInfo, options); + customConverter = GetCustomConverterForMember(typeToConvert, memberInfo, options); } catch (InvalidOperationException) when (ignoreCondition == JsonIgnoreCondition.Always) { diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs index 867974f..c9be69d 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs @@ -22,6 +22,21 @@ namespace System.Text.Json.Serialization.Tests } [Theory] + [InlineData(typeof(ClassWithCustomNamedInitOnlyProperty))] + [InlineData(typeof(StructWithCustomNamedInitOnlyProperty))] + public virtual async Task CustomNamedInitOnlyProperties(Type type) + { + // Regression test for https://github.com/dotnet/runtime/issues/82730 + + // Init-only property included by default. + object obj = await Serializer.DeserializeWrapper(@"{""CustomMyInt"":1}", type); + Assert.Equal(1, (int)type.GetProperty("MyInt").GetValue(obj)); + + // Init-only properties can be serialized. + Assert.Equal(@"{""CustomMyInt"":1}", await Serializer.SerializeWrapper(obj)); + } + + [Theory] [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter))] [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter))] [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] @@ -59,6 +74,18 @@ namespace System.Text.Json.Serialization.Tests public int MyInt { get; init; } } + public class ClassWithCustomNamedInitOnlyProperty + { + [JsonPropertyName("CustomMyInt")] + public int MyInt { get; init; } + } + + public struct StructWithCustomNamedInitOnlyProperty + { + [JsonPropertyName("CustomMyInt")] + public int MyInt { get; init; } + } + public class Class_PropertyWith_PrivateInitOnlySetter { public int MyInt { get; private init; } diff --git a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs index 4af913c..9540db0 100644 --- a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs @@ -568,6 +568,28 @@ namespace System.Text.Json.Serialization.Tests public required string SomeProperty { get; set; } } + [Fact] + public async Task ClassWithCustomRequiredPropertyName_Roundtrip() + { + // Regression test for https://github.com/dotnet/runtime/issues/82730 + var value = new ClassWithCustomRequiredPropertyName { Property = 42, PropertyWithInitOnlySetter = 43 }; + string json = await Serializer.SerializeWrapper(value); + Assert.Equal("""{"Prop":42,"PropWithInit":43}""", json); + + value = await Serializer.DeserializeWrapper(json); + Assert.Equal(42, value.Property); + Assert.Equal(43, value.PropertyWithInitOnlySetter); + } + + public class ClassWithCustomRequiredPropertyName + { + [JsonPropertyName("Prop")] + public required int Property { get; set; } + + [JsonPropertyName("PropWithInit")] + public required int PropertyWithInitOnlySetter { get; init; } + } + 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/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs index b63c3ea..1a219da 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 @@ -162,6 +162,8 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(ClassWithMissingObjectProperty))] [JsonSerializable(typeof(ClassWithInitOnlyProperty))] [JsonSerializable(typeof(StructWithInitOnlyProperty))] + [JsonSerializable(typeof(ClassWithCustomNamedInitOnlyProperty))] + [JsonSerializable(typeof(StructWithCustomNamedInitOnlyProperty))] [JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))] [JsonSerializable(typeof(ClassWithNonPublicProperties))] [JsonSerializable(typeof(ClassWithProperty_IgnoreConditionAlways))] @@ -404,6 +406,8 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(ClassWithMissingObjectProperty))] [JsonSerializable(typeof(ClassWithInitOnlyProperty))] [JsonSerializable(typeof(StructWithInitOnlyProperty))] + [JsonSerializable(typeof(ClassWithCustomNamedInitOnlyProperty))] + [JsonSerializable(typeof(StructWithCustomNamedInitOnlyProperty))] [JsonSerializable(typeof(MyClassWithValueTypeInterfaceProperty))] [JsonSerializable(typeof(ClassWithNonPublicProperties))] [JsonSerializable(typeof(ClassWithProperty_IgnoreConditionAlways))] 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 64a946c..58cae74 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 @@ -28,6 +28,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(ClassWithRequiredField))] [JsonSerializable(typeof(ClassWithRequiredExtensionDataProperty))] [JsonSerializable(typeof(ClassWithRequiredKeywordAndJsonRequiredCustomAttribute))] + [JsonSerializable(typeof(ClassWithCustomRequiredPropertyName))] internal sealed partial class RequiredKeywordTestsContext : JsonSerializerContext { } -- 2.7.4