[API Proposal]: allow multiple JsonStringEnumMemberNameAttribute Names
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
**Background**
Using `JsonStringEnumMemberNameAttribute`, I want to deserialize multiple different strings to a single Enum member. Right now, only one `JsonStringEnumMemberName` can be used per enum member. It's very natural to want to do this:
```csharp
enum TranportationMode
{
Truck,
Ocean,
[JsonStringEnumMemberName("Air Freight")]
[JsonStringEnumMemberName("Air Cargo")] //This throws an error, as AllowMultiple = false
AirCargo
}
```
This would work for deserialization, as there's no indeterminism to worry about. But for serialization, we're missing a way to set a "primary" string value.
**Motivation:**
Here are some reasons why one would want multiple strings to deserialize to a single enum member.
- Keeping backwards deserialization compatibility when changing the representative string value of the enum member.
- Removes the need to write and hook up a custom json serializer
- Useful in mapping from one set of string enums to another set of enums.
- Especially useful in the context of .net api model binding.
### API Proposal
Add a `params string[] names` constructor to `JsonStringEnumMemberNameAttribute`.
```diff
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class JsonStringEnumMemberNameAttribute : JsonAttribute
{
public string Name { get; }
+ public string[]? AlternateNames { get; init; }
public JsonStringEnumMemberNameAttribute (string name)
{
Name = name;
}
+ public JsonStringEnumMemberNameAttribute (params string[] names)
+ {
+ Name = names[0];
+ AlternateNames = names[1..^0];
+ }
}
```
### API Usage
```csharp
enum TransportationMode
{
Truck,
Ocean,
[JsonStringEnumMemberName("Air Cargo", "Air Freight")]
AirCargo
}
```
When deserializing a string to enum, the deserializer would check all names in the list for a match. When serializing, it would take the first item in the list, so "Air Cargo" here. Runtime/aot validation (or an analyzer) would throw if there are any overlaps of string names for different enum members.
Alternative usage if additional clarity is desired
```csharp
enum TransportationMode
{
Truck,
Ocean,
[JsonStringEnumMemberName("Air Cargo", AlternateNames = ["Air Freight"])]
AirCargo
}
```
### Alternative Designs
Not as clean, but could still work:
Change the attribute to allow multiple. It's very natural for a dev to try this. Serialization is nondeterministic because attribute order is not guaranteed. Could add an optional `Primary` flag for serialization.
```csharp
enum TranportationMode
{
Truck,
Ocean,
[JsonStringEnumMemberName("Air Freight", Primary = true)]
[JsonStringEnumMemberName("Air Cargo")]
AirCargo
}
```
### Risks
Backwards compatibility is maintained since the single `string` constructor in use today takes precedence over the `params string[]` constructor.
Contributor guide
Research direction
The issue names JsonStringEnumMemberNameAttribute and enum serialization/deserialization as the entry points; begin by locating the attribute and current enum name handling. Confirm the API shape and serialization behavior, then add coverage for alternate names and overlapping-name validation if the design is accepted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100