Skip to content

System.Text.Json contract customization: stack overflow when adding recursive properties via JsonMetadataServices #72175

Closed
@eiriktsarpalis

Description

@eiriktsarpalis

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.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions