Skip to content

Commit d25158d

Browse files
Ensure reflection property metadata is instantiated lazily (#76828) (#76869)
* Ensure reflection property metadata is instantiated lazily. * Address feedback
1 parent 51758ee commit d25158d

File tree

3 files changed

+75
-16
lines changed

3 files changed

+75
-16
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,6 @@ private void PopulatePropertyList()
737737
{
738738
if (!_isConfigured)
739739
{
740-
// Ensure SourceGen had a chance to add properties
741740
LateAddProperties();
742741
_properties = new(this);
743742
return;
@@ -882,12 +881,7 @@ internal void InitializePropertyCache()
882881
}
883882
else
884883
{
885-
// Resolver didn't modify properties
886-
887-
// Source gen currently when initializes properties
888-
// also assigns JsonPropertyInfo's JsonTypeInfo which causes SO if there are any
889-
// cycles in the object graph. For that reason properties cannot be added immediately.
890-
// This is a no-op for ReflectionJsonTypeInfo
884+
// Resolver didn't modify any properties, create the property cache from scratch.
891885
LateAddProperties();
892886
PropertyCache ??= CreatePropertyCache(capacity: 0);
893887
}

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o
2323
PopulatePolymorphismMetadata();
2424
MapInterfaceTypesToCallbacks();
2525

26-
if (PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object)
27-
{
28-
AddPropertiesAndParametersUsingReflection();
29-
}
30-
3126
Func<object>? createObject = Options.MemberAccessorStrategy.CreateConstructor(typeof(T));
3227
SetCreateObjectIfCompatible(createObject);
3328
CreateObjectForExtensionDataProperty = createObject;
@@ -37,11 +32,17 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o
3732
converter.ConfigureJsonTypeInfoUsingReflection(this, options);
3833
}
3934

40-
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
41-
[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
42-
private void AddPropertiesAndParametersUsingReflection()
35+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:RequiresUnreferencedCode",
36+
Justification = "The ctor is marked RequiresUnreferencedCode.")]
37+
internal override void LateAddProperties()
4338
{
44-
Debug.Assert(PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object);
39+
Debug.Assert(!IsConfigured);
40+
Debug.Assert(PropertyCache is null);
41+
42+
if (Kind != JsonTypeInfoKind.Object)
43+
{
44+
return;
45+
}
4546

4647
const BindingFlags BindingFlags =
4748
BindingFlags.Instance |

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonPropertyInfo.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,5 +1700,69 @@ public string? Value
17001700
set => _value = value ?? "NULL";
17011701
}
17021702
}
1703+
1704+
[Fact]
1705+
public static void TypeWithIgnoredUnsupportedType_ShouldBeSupported()
1706+
{
1707+
// Regression test for https://github.com/dotnet/runtime/issues/76807
1708+
1709+
// Sanity check -- metadata resolution for the unsupported type is failing
1710+
Assert.Throws<InvalidOperationException>(() => JsonSerializerOptions.Default.GetTypeInfo(typeof(UnsupportedType)));
1711+
1712+
// Serialization works as expected
1713+
string json = JsonSerializer.Serialize(new PocoWithIgnoredUnsupportedType());
1714+
JsonTestHelper.AssertJsonEqual("{}", json);
1715+
1716+
// Metadata is reported as expected
1717+
JsonTypeInfo jti = JsonSerializerOptions.Default.GetTypeInfo(typeof(PocoWithIgnoredUnsupportedType));
1718+
Assert.Equal(1, jti.Properties.Count);
1719+
JsonPropertyInfo propertyInfo = jti.Properties[0];
1720+
Assert.Null(propertyInfo.Get);
1721+
Assert.Null(propertyInfo.Set);
1722+
}
1723+
1724+
public class PocoWithIgnoredUnsupportedType
1725+
{
1726+
[JsonIgnore]
1727+
public UnsupportedType UnsuportedProperty { get; set; }
1728+
}
1729+
1730+
[Fact]
1731+
public static void TypeWithUnIgnoredUnsupportedType_CanModifyUnsupportedType()
1732+
{
1733+
var options = new JsonSerializerOptions
1734+
{
1735+
TypeInfoResolver = new DefaultJsonTypeInfoResolver
1736+
{
1737+
Modifiers =
1738+
{
1739+
static jti =>
1740+
{
1741+
if (jti.Type == typeof(PocoWithUnIgnoredUnsupportedType))
1742+
{
1743+
Assert.Equal(1, jti.Properties.Count);
1744+
JsonPropertyInfo propertyInfo = jti.Properties[0];
1745+
Assert.Equal(typeof(UnsupportedType), propertyInfo.PropertyType);
1746+
1747+
jti.Properties.Clear();
1748+
}
1749+
}
1750+
}
1751+
}
1752+
};
1753+
1754+
string json = JsonSerializer.Serialize(new PocoWithUnIgnoredUnsupportedType(), options);
1755+
JsonTestHelper.AssertJsonEqual("{}", json);
1756+
}
1757+
1758+
public class PocoWithUnIgnoredUnsupportedType
1759+
{
1760+
public UnsupportedType UnsuportedProperty { get; set; }
1761+
}
1762+
1763+
public class UnsupportedType
1764+
{
1765+
public ReadOnlySpan<byte> Span => Array.Empty<byte>();
1766+
}
17031767
}
17041768
}

0 commit comments

Comments
 (0)