aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Minimal API: JsonStringEnumMemberName doesn't work while deserializing query params & headers

Open vengut opened this issue 6 months ago • 1 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Describe the bug

I used this new .NET 9 attribute to customize enum names and it errors out during request parsing/deserialization: http://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/customize-properties#custom-enum-member-names

Expected Behavior

I set up my enum like this:

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum TestEnum
{
    [JsonStringEnumMemberName("one")]
    A1,
    [JsonStringEnumMemberName("two")]
    A2,
}

I expected this call to not error out http://localhost:5274/?testEnum=one

All of these work: http://localhost:5274/?testEnum=A1 http://localhost:5274/?testEnum=0

This also works:

Console.WriteLine(JsonSerializer.Deserialize<TestEnum>("\"one\"")); // A1

Steps To Reproduce

Program.cs:

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
var app = builder.Build();

app.MapGet("/", ([FromQuery] TestEnum? testEnum = null) =>
{
    Console.WriteLine(testEnum);
    Console.WriteLine(JsonSerializer.Deserialize<TestEnum>("\"one\""));
    return testEnum;
});

app.Run();

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum TestEnum
{
    [JsonStringEnumMemberName("one")]
    A1,
    [JsonStringEnumMemberName("two")]
    A2,
}

csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net9.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>

</Project>

Exceptions (if any)

400

.NET Version

9.0.300

Anything else?

No response

vengut avatar Jun 12 '25 22:06 vengut

Update:

Parsing from body works fine.

app.MapPost("/", ([FromBody] TestEnum? testEnum = null) =>
{
    Console.WriteLine(testEnum);
    Console.WriteLine(JsonSerializer.Deserialize<TestEnum>("\"one\""));
    return testEnum;
});

However, header doesn't.

app.MapPost("/", ([FromHeader(Name = "test-enum")] TestEnum? testEnum = null) =>
{
    Console.WriteLine(testEnum);
    Console.WriteLine(JsonSerializer.Deserialize<TestEnum>("\"one\""));
    return testEnum;
});

vengut avatar Jun 14 '25 17:06 vengut

Issue confirmed with .NET 9.0.6. The only workaround found is to use a specific IModelBinder and do the manual conversion there from bindingContext.HttpContext.Request.Query; a bit ugly.

IBlcl avatar Jun 19 '25 10:06 IBlcl

This is expected behavior. The attributes are specific to System.Text.JSON's serializer and only apply when when deserialize the body as JSON.

Resolving from the query or route uses simple 'TryParse' binding to bind the enum so the rules don't apply.

To customize behavior for binding from the route, you'll want to take advantage of BindAsync for custom binding.

captainsafia avatar Jul 25 '25 05:07 captainsafia