Description
Minimal repro:
[Fact]
public static void AddRecursiveJsonPropertyInfoFromMetadataServices()
{
JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
static typeInfo =>
{
if (typeInfo.Type != typeof(SomeClass))
{
return;
}
typeInfo.Properties.Clear();
JsonPropertyInfo propertyInfo = JsonMetadataServices.CreatePropertyInfo<SomeClass>(
typeInfo.Options,
new JsonPropertyInfoValues<SomeClass>()
{
DeclaringType = typeof(SomeClass),
PropertyName = "test",
});
typeInfo.Properties.Add(propertyInfo);
Assert.Equal(JsonTypeInfoKind.Object, typeInfo.Kind);
}
}
}
};
JsonTypeInfo<SomeClass> jsonTypeInfo = (JsonTypeInfo<SomeClass>)options.GetTypeInfo(typeof(SomeClass));
}
This was probably introduced by #71908: the DetermineSerializationCapabilities
method used to determine whether read-only properties should be ignored was moved out of the final Configure
stage in order to make it user-customizable. However, determining the right policy requires eager resolution of the effective converter for the given property, so in certain situations (like the one shared above) this can trigger endless recursive resolution of JsonTypeInfo
for the parent type.
Ultimately, this is a bootstrapping issue that is not possible to fix. One possibility might be to change the policy so that read-only properties are not ignored depending on the converter strategy, but ultimately this would be breaking change that we should not be entertaining at this stage. Realistically, I think we should revert aspects of #71908 so that the ignore policy is calculated post-configuration. Even though this would end up overriding user-specified contract configuration again, it would only impact JsonPropertyInfo
instances emanating from reflection or JsonMetadataServices
sources and it should be possible to work around the issue.