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

JsonNumberEnumConverter writes string property names #102120

Closed
VelvetToroyashi opened this issue May 12, 2024 · 4 comments
Closed

JsonNumberEnumConverter writes string property names #102120

VelvetToroyashi opened this issue May 12, 2024 · 4 comments

Comments

@VelvetToroyashi
Copy link

Description

Right now, when using System.Text.Json, adding a JsonNumberEnumConverter to the converter list, the resulting JSON when serializing a dictionary is that the enum is written as a string value with the enum's name instead of the string representation of the value of the enum.

Reproduction Steps

Add the JsonNumberEnumConverter to the serializer options, and serialize an object.

var options = new JsonSerializerOptions();

options.Converters.Insert(0, new JsonNumberEnumConverter<TestEnum>());

var output = JsonSerializer.Serialize(new { TestEnum.FirstValue = new { } }, options);

public enum TestEnum
{
    FirstValue = 0,
    SecondValue = 1
}

Expected behavior

STJ writes a sting-ified integer as the dictionary key, resulting in the following JSON:

{"0":{}}

Actual behavior

The enum name is written for the key instead:

{"FirstValue":{}}

Regression?

No response

Known Workarounds

Writing a custom converter for dictionaries that writes the enum values as string integers is the route we've gone for now:

https://github.com/Remora/Remora.Discord/blob/e1ae7617c587fcce41d350c43c00b194797743df/Backend/Remora.Discord.API/Json/Converters/Internal/EnumIntKeyDictionaryConverterFactory.cs#L114-L124

Though this relies on having a key converter which can turn the enum into a string key, as well as converting the whole dictionary, which can make it cumbersome to work with.

Configuration

.NET 8.0.100
Linux Mint 21.3 x86_64
Platform agnostic

Other information

This is likely a very niche issue (I couldn't find any other issue referencing this behavior being problematic.

The converter does appear to have logic supporting writing values as integer keys, however this only appears to be if the value written falls out of range of the enum itself:

switch (s_enumTypeCode)
{
// Use Unsafe.As instead of raw pointers for .NET Standard support.
// https://github.com/dotnet/runtime/issues/84895
case TypeCode.Int32:
writer.WritePropertyName(Unsafe.As<T, int>(ref value));
break;

I'd also like to note that this is only an issue because of Discord, whom is using the same enum for both arrays and dictionaries.

From their documentation, using a dictionary for integration_types_config: https://discord.com/developers/docs/resources/application#edit-current-application

And an array for integration_types: https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure

Both of these use the ApplicationIntegrationType enum, but one is serialized as integers [0, 1] and the other as string representations of those values {"0": { ... } }

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 12, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis
See info in area-owners.md if you want to be subscribed.

@eiriktsarpalis
Copy link
Member

This is the default serialization behavior of enum types when serialized as dictionary keys, consider

var value = new Dictionary<TestEnum, TestEnum> { [TestEnum.FirstValue] = TestEnum.FirstValue };
var output = JsonSerializer.Serialize(value);
Console.WriteLine(output); // {"FirstValue":0}

public enum TestEnum
{
    FirstValue = 0,
    SecondValue = 1
}

The JsonNumberEnumConverter<T> converters enacts the default enum serialization semantics, and exists there merely as a fallback for cases where string enum serialization has been enabled globally (e.g. via the JsonStringEnumConverter type).

Arguably this behavior is surprising, but at the same time it's shipped behavior so changing it today would count as a breaking change. As a workaround you might consider authoring a custom converter type while also overriding the WriteAsPropertyName/ReadAsPropertyName methods that govern dictionary key serialization specifically.

@eiriktsarpalis eiriktsarpalis closed this as not planned Won't fix, can't repro, duplicate, stale May 12, 2024
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label May 12, 2024
@VelvetToroyashi
Copy link
Author

Kind of figured that changing it outright would be a breaking change; I was curious if adopting a new parameter to configure this behavior specifically was out of the question, however if the recommendation is to make a custom converter, I suppose that's the route we'll take (and, even if this were changed, it'd be an issue for multi-targeting projects like the one I linked, bwah).

And by the custom converter, I suppose you mean a converter specifically for the enum type that implements the aforementioned method? Or was that meant to be interpreted as a dictionary converter (like the one I linked), thanks!

@eiriktsarpalis
Copy link
Member

And by the custom converter, I suppose you mean a converter specifically for the enum type that implements the aforementioned method?

Yep 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants