Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add annotations and regression testing for members accessed by legacy schema generation. #109424

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class EnumConverter<T> : JsonPrimitiveConverter<T>
internal sealed class EnumConverter<T> : JsonPrimitiveConverter<T> // Do not rename FQN (legacy schema generation)
where T : struct, Enum
{
private static readonly TypeCode s_enumTypeCode = Type.GetTypeCode(typeof(T));
Expand All @@ -22,9 +22,8 @@ internal sealed class EnumConverter<T> : JsonPrimitiveConverter<T>
private static readonly bool s_isSignedEnum = ((int)s_enumTypeCode % 2) == 1;
private static readonly bool s_isFlagsEnum = typeof(T).IsDefined(typeof(FlagsAttribute), inherit: false);

private readonly EnumConverterOptions _converterOptions;

private readonly JsonNamingPolicy? _namingPolicy;
private readonly EnumConverterOptions _converterOptions; // Do not rename (legacy schema generation)
private readonly JsonNamingPolicy? _namingPolicy; // Do not rename (legacy schema generation)

/// <summary>
/// Stores metadata for the individual fields declared on the enum.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace System.Text.Json.Serialization.Converters
{
[Flags]
internal enum EnumConverterOptions
internal enum EnumConverterOptions // Do not modify (legacy schema generation)
{
/// <summary>
/// Allow string values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class NullableConverter<T> : JsonConverter<T?> where T : struct
internal sealed class NullableConverter<T> : JsonConverter<T?> where T : struct // Do not rename FQN (legacy schema generation)
{
internal override Type? ElementType => typeof(T);
internal override JsonConverter? NullableElementConverter => _elementConverter;
Expand All @@ -15,7 +15,7 @@ internal sealed class NullableConverter<T> : JsonConverter<T?> where T : struct

// It is possible to cache the underlying converter since this is an internal converter and
// an instance is created only once for each JsonSerializerOptions instance.
private readonly JsonConverter<T> _elementConverter;
private readonly JsonConverter<T> _elementConverter; // Do not rename (legacy schema generation)

public NullableConverter(JsonConverter<T> elementConverter)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public JsonObjectCreationHandling? ObjectCreationHandling
private JsonObjectCreationHandling? _objectCreationHandling;
internal JsonObjectCreationHandling EffectiveObjectCreationHandling { get; private set; }

internal string? MemberName { get; set; }
internal string? MemberName { get; set; } // Do not modify (legacy schema generation)
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
internal MemberTypes MemberType { get; set; }
internal bool IsVirtual { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,52 @@ public void JsonSchemaExporterOptions_Default_IsSame()
Assert.Same(JsonSchemaExporterOptions.Default, JsonSchemaExporterOptions.Default);
}

#if !BUILDING_SOURCE_GENERATOR_TESTS
[Fact]
public void LegacySchemaExporter_CanAccessReflectedMembers()
{
// A number of libraries such as Microsoft.Extensions.AI and Semantic Kernel
// rely on a polyfilled version of JsonSchemaExporter for System.Text.Json v8
// that uses private reflection to access necessary metadata. This test validates
// that the necessary members are still present in newer implementations of STJ.

JsonStringEnumConverter converter = new(namingPolicy: JsonNamingPolicy.CamelCase, allowIntegerValues: false);
JsonSerializerOptions options = new(JsonSerializerOptions.Default) { Converters = { converter } };
JsonConverter nullableConverter = options.GetConverter(typeof(BindingFlags?));

Type nullableConverterType = nullableConverter.GetType();
Assert.True(nullableConverterType.IsGenericType);
Assert.StartsWith("System.Text.Json.Serialization.Converters.NullableConverter`1", nullableConverterType.FullName);

FieldInfo elementConverterField = nullableConverterType.GetField("_elementConverter", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(elementConverterField);
var enumConverter = (JsonConverter)elementConverterField.GetValue(nullableConverter);
Assert.NotNull(enumConverter);

Type enumConverterType = enumConverter.GetType();
Assert.True(enumConverterType.IsGenericType);
Assert.StartsWith("System.Text.Json.Serialization.Converters.EnumConverter`1", enumConverterType.FullName);

FieldInfo namingPolicyField = enumConverterType.GetField("_namingPolicy", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(namingPolicyField);
Assert.Same(JsonNamingPolicy.CamelCase, namingPolicyField.GetValue(enumConverter));

FieldInfo converterOptionsField = enumConverterType.GetField("_converterOptions", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(converterOptionsField);
Assert.Equal(1, (int)converterOptionsField.GetValue(enumConverter));

JsonPropertyInfo propertyInfo = PocoWithPropertyContext.Default.PocoWithProperty.Properties.Single();
PropertyInfo memberNameProperty = typeof(JsonPropertyInfo).GetProperty("MemberName", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(memberNameProperty);
Assert.Equal("Value", memberNameProperty.GetValue(propertyInfo));
}

record PocoWithProperty(int Value);

[JsonSerializable(typeof(PocoWithProperty))]
partial class PocoWithPropertyContext : JsonSerializerContext;
#endif

protected void AssertValidJsonSchema(Type type, string expectedJsonSchema, JsonNode actualJsonSchema)
{
JsonNode? expectedJsonSchemaNode = JsonNode.Parse(expectedJsonSchema, documentOptions: new() { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true });
Expand Down
Loading