Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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 @@ -182,7 +182,27 @@ private static JsonSchema MapJsonSchemaCore(
JsonTypeInfo elementTypeInfo = typeInfo.Options.GetTypeInfo(elementConverter.Type!);
schema = MapJsonSchemaCore(ref state, elementTypeInfo, customConverter: elementConverter, cacheResult: false);

if (schema.Enum != null)
if (elementConverter.IsIeeeFloatingPointConverter &&
(effectiveNumberHandling & JsonNumberHandling.AllowNamedFloatingPointLiterals) != 0)
{
// IEEE floating-point types with AllowNamedFloatingPointLiterals generate an anyOf schema.
// Fold null into the numeric branch to preserve nullability for nullable wrappers.
Debug.Assert(schema.AnyOf is not null, "IEEE floating-point types with AllowNamedFloatingPointLiterals should generate an anyOf schema.");

List<JsonSchema> anyOf = schema.AnyOf;
Debug.Assert(anyOf.Exists(b => (b.Type & JsonSchemaType.Number) != 0),
"IEEE floating-point anyOf schema should have a numeric branch.");

foreach (JsonSchema branch in anyOf)
{
if ((branch.Type & JsonSchemaType.Number) != 0)
{
branch.Type |= JsonSchemaType.Null;
break;
}
}
}
else if (schema.Enum != null)
{
Debug.Assert(elementTypeInfo.Type.IsEnum, "The enum keyword should only be populated by schemas for enum types.");
schema.Enum.Add(null); // Append null to the enum array.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public DoubleConverter()
IsInternalConverterForNumberType = true;
}

internal override bool IsIeeeFloatingPointConverter => true;

public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (options?.NumberHandling is not null and not JsonNumberHandling.Strict)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public HalfConverter()
IsInternalConverterForNumberType = true;
}

internal override bool IsIeeeFloatingPointConverter => true;

public override Half Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (options?.NumberHandling is not null and not JsonNumberHandling.Strict)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public SingleConverter()
IsInternalConverterForNumberType = true;
}

internal override bool IsIeeeFloatingPointConverter => true;

public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (options?.NumberHandling is not null and not JsonNumberHandling.Strict)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ internal JsonConverter<TTarget> CreateCastingConverter<TTarget>()
/// </summary>
internal bool IsValueType { get; init; }

/// <summary>
/// Indicates whether this converter handles IEEE 754 floating-point types
/// (double, float, Half) that may emit anyOf schemas with named floating-point
/// literals under AllowNamedFloatingPointLiterals.
/// </summary>
internal virtual bool IsIeeeFloatingPointConverter => false;

/// <summary>
/// Whether the converter is built-in.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,73 @@ public static IEnumerable<ITestData> GetTestDataCore()
}
""");

// Regression test for https://github.com/dotnet/runtime/issues/129432
// Nullable floating-point types under AllowNamedFloatingPointLiterals must retain the null branch.
yield return new TestData<double?>(
Comment thread
eiriktsarpalis marked this conversation as resolved.
Value: 3.14,
AdditionalValues: [null, double.NaN, double.PositiveInfinity, double.NegativeInfinity],
Comment thread
eiriktsarpalis marked this conversation as resolved.
ExpectedJsonSchema: """
{
"anyOf": [
{ "type": ["number", "null"] },
{ "enum": ["NaN", "Infinity", "-Infinity"] }
]
}
""",
SerializerOptions: new() { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals });

yield return new TestData<float?>(
Value: 1.2f,
AdditionalValues: [null, float.NaN, float.PositiveInfinity, float.NegativeInfinity],
ExpectedJsonSchema: """
{
"anyOf": [
{ "type": ["number", "null"] },
{ "enum": ["NaN", "Infinity", "-Infinity"] }
]
}
""",
SerializerOptions: new() { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals });

#if NET
yield return new TestData<Half?>(
Value: (Half)1.5,
AdditionalValues: [null, Half.NaN, Half.PositiveInfinity, Half.NegativeInfinity],
ExpectedJsonSchema: """
{
"anyOf": [
{ "type": ["number", "null"] },
{ "enum": ["NaN", "Infinity", "-Infinity"] }
]
}
""",
SerializerOptions: new() { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals });
#endif

yield return new TestData<PocoWithNullableFloatingPoint>(
Value: new() { Latitude = 3.14, Longitude = 1.2f },
AdditionalValues: [new() { Latitude = null, Longitude = null }],
ExpectedJsonSchema: """
{
"type": ["object","null"],
"properties": {
"Latitude": {
"anyOf": [
{ "type": ["number", "null"] },
{ "enum": ["NaN", "Infinity", "-Infinity"] }
]
},
"Longitude": {
"anyOf": [
{ "type": ["number", "null"] },
{ "enum": ["NaN", "Infinity", "-Infinity"] }
]
}
}
}
""",
SerializerOptions: new() { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals });

yield return new TestData<PocoWithRecursiveMembers>(
Value: new() { Value = 1, Next = new() { Value = 2, Next = new() { Value = 3 } } },
AdditionalValues: [new() { Value = 1, Next = null }],
Expand Down Expand Up @@ -1290,6 +1357,12 @@ public class PocoWithCustomNumberHandlingOnProperties
public decimal DecimalAllowingFloatingPointLiteralsAndReadingFromString { get; set; }
}

public class PocoWithNullableFloatingPoint
{
public double? Latitude { get; set; }
public float? Longitude { get; set; }
}

public class PocoWithRecursiveMembers
{
public int Value { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public sealed partial class JsonSchemaExporterTests_SourceGen()
[JsonSerializable(typeof(bool?))]
[JsonSerializable(typeof(int?))]
[JsonSerializable(typeof(double?))]
[JsonSerializable(typeof(float?))]
#if NET
[JsonSerializable(typeof(Half?))]
#endif
[JsonSerializable(typeof(Guid?))]
[JsonSerializable(typeof(JsonElement?))]
[JsonSerializable(typeof(IntEnum?))]
Expand All @@ -87,6 +91,7 @@ public sealed partial class JsonSchemaExporterTests_SourceGen()
[JsonSerializable(typeof(PocoWithCustomNaming))]
[JsonSerializable(typeof(PocoWithCustomNumberHandling))]
[JsonSerializable(typeof(PocoWithCustomNumberHandlingOnProperties))]
[JsonSerializable(typeof(PocoWithNullableFloatingPoint))]
[JsonSerializable(typeof(PocoWithRecursiveMembers))]
[JsonSerializable(typeof(PocoWithRecursiveCollectionElement))]
[JsonSerializable(typeof(PocoWithRecursiveDictionaryValue))]
Expand Down
Loading