Skip to content

JsonSourceGenerationOptionsAttribute constructor is out of sync with the JsonSerializerOptions constructor #122757

@SnipUndercover

Description

@SnipUndercover

Description

As the title suggests, JsonSourceGenerationOptionsAttribute(JsonSerializerDefaults defaults) is out of sync with JsonSerializerOptions(JsonSerializerDefaults defaults), despite the comment suggesting otherwise.
This leads to an ArgumentOutOfRangeException when the type is annotated with [JsonSourceGenerationOptionsAttribute(JsonSerializerDefaults.Strict)].

Reproduction Steps

using System.Text.Json;
using System.Text.Json.Serialization;

internal static class Reproduction
{
    public static void Main()
    {
        // Crash at runtime
        _ = typeof(SourceGenerationContext).GetCustomAttributes(true);
    }
}

[JsonSourceGenerationOptions(JsonSerializerDefaults.Strict)]
[JsonSerializable(typeof(Example))]
internal partial class SourceGenerationContext : JsonSerializerContext;

internal class Example;

Expected behavior

The returned array contains the JsonSourceGenerationOptions attribute with its properties configured analogously to a new JsonSerializerOptions(JsonSerializerDefaults.Strict).

Actual behavior

The program crashes with the following message, despite the enum value being valid:

System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'defaults')

Regression?

No

Known Workarounds

Assign the attribute properties manually, referencing the JsonSerializerOptions(JsonSerializerDefaults defaults) constructor.

Configuration

  • .NET 10.0.101
  • Windows 11 24H2 (build 26100.7462)
  • x64 architecture

The issue is specific to versions of .NET 10 past and including .NET 10 Preview 6.

Other information

The JsonSerializerDefaults.Strict enum value was implemented by #116271. The lack of support in the JsonSourceGenerationOptionsAttribute is likely an oversight.

Relevant code

public JsonSerializerOptions(JsonSerializerDefaults defaults) : this()
{
// Should be kept in sync with equivalent overload in JsonSourceGenerationOptionsAttribute
if (defaults == JsonSerializerDefaults.Web)
{
_propertyNameCaseInsensitive = true;
_jsonPropertyNamingPolicy = JsonNamingPolicy.CamelCase;
_numberHandling = JsonNumberHandling.AllowReadingFromString;
}
else if (defaults == JsonSerializerDefaults.Strict)
{
_unmappedMemberHandling = JsonUnmappedMemberHandling.Disallow;
_allowDuplicateProperties = false;
_respectNullableAnnotations = true;
_respectRequiredConstructorParameters = true;
}
else if (defaults != JsonSerializerDefaults.General)
{
throw new ArgumentOutOfRangeException(nameof(defaults));
}
}

public JsonSourceGenerationOptionsAttribute(JsonSerializerDefaults defaults)
{
// Constructor kept in sync with equivalent overload in JsonSerializerOptions
if (defaults is JsonSerializerDefaults.Web)
{
PropertyNameCaseInsensitive = true;
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase;
NumberHandling = JsonNumberHandling.AllowReadingFromString;
}
else if (defaults is not JsonSerializerDefaults.General)
{
throw new ArgumentOutOfRangeException(nameof(defaults));
}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions