diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index ef345aa231d6b..38868e5c1e7ab 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -737,7 +737,6 @@ private void PopulatePropertyList() { if (!_isConfigured) { - // Ensure SourceGen had a chance to add properties LateAddProperties(); _properties = new(this); return; @@ -882,12 +881,7 @@ internal void InitializePropertyCache() } else { - // Resolver didn't modify properties - - // Source gen currently when initializes properties - // also assigns JsonPropertyInfo's JsonTypeInfo which causes SO if there are any - // cycles in the object graph. For that reason properties cannot be added immediately. - // This is a no-op for ReflectionJsonTypeInfo + // Resolver didn't modify any properties, create the property cache from scratch. LateAddProperties(); PropertyCache ??= CreatePropertyCache(capacity: 0); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs index 72fb5a48ac0ab..e56e1fae3a20e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs @@ -23,11 +23,6 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o PopulatePolymorphismMetadata(); MapInterfaceTypesToCallbacks(); - if (PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object) - { - AddPropertiesAndParametersUsingReflection(); - } - Func? createObject = Options.MemberAccessorStrategy.CreateConstructor(typeof(T)); SetCreateObjectIfCompatible(createObject); CreateObjectForExtensionDataProperty = createObject; @@ -37,11 +32,17 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o converter.ConfigureJsonTypeInfoUsingReflection(this, options); } - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private void AddPropertiesAndParametersUsingReflection() + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void LateAddProperties() { - Debug.Assert(PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object); + Debug.Assert(!IsConfigured); + Debug.Assert(PropertyCache is null); + + if (Kind != JsonTypeInfoKind.Object) + { + return; + } const BindingFlags BindingFlags = BindingFlags.Instance | diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs index cb8c53ae64624..84939076aa9bc 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs @@ -1700,5 +1700,69 @@ public string? Value set => _value = value ?? "NULL"; } } + + [Fact] + public static void TypeWithIgnoredUnsupportedType_ShouldBeSupported() + { + // Regression test for https://github.com/dotnet/runtime/issues/76807 + + // Sanity check -- metadata resolution for the unsupported type is failing + Assert.Throws(() => JsonSerializerOptions.Default.GetTypeInfo(typeof(UnsupportedType))); + + // Serialization works as expected + string json = JsonSerializer.Serialize(new PocoWithIgnoredUnsupportedType()); + JsonTestHelper.AssertJsonEqual("{}", json); + + // Metadata is reported as expected + JsonTypeInfo jti = JsonSerializerOptions.Default.GetTypeInfo(typeof(PocoWithIgnoredUnsupportedType)); + Assert.Equal(1, jti.Properties.Count); + JsonPropertyInfo propertyInfo = jti.Properties[0]; + Assert.Null(propertyInfo.Get); + Assert.Null(propertyInfo.Set); + } + + public class PocoWithIgnoredUnsupportedType + { + [JsonIgnore] + public UnsupportedType UnsuportedProperty { get; set; } + } + + [Fact] + public static void TypeWithUnIgnoredUnsupportedType_CanModifyUnsupportedType() + { + var options = new JsonSerializerOptions + { + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + static jti => + { + if (jti.Type == typeof(PocoWithUnIgnoredUnsupportedType)) + { + Assert.Equal(1, jti.Properties.Count); + JsonPropertyInfo propertyInfo = jti.Properties[0]; + Assert.Equal(typeof(UnsupportedType), propertyInfo.PropertyType); + + jti.Properties.Clear(); + } + } + } + } + }; + + string json = JsonSerializer.Serialize(new PocoWithUnIgnoredUnsupportedType(), options); + JsonTestHelper.AssertJsonEqual("{}", json); + } + + public class PocoWithUnIgnoredUnsupportedType + { + public UnsupportedType UnsuportedProperty { get; set; } + } + + public class UnsupportedType + { + public ReadOnlySpan Span => Array.Empty(); + } } }