System.Text.Json
enum serialization
custom enum names
JSON mapping
.NET serialization

System.Text.Json How do I specify a custom name for an enum value?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

System.Text.Json serializes enums as integers by default. To serialize as strings, use JsonStringEnumConverter. To customize the string name for individual enum values, use the JsonStringEnumMemberName attribute (.NET 9+) or the [EnumMember] attribute with a custom converter. Before .NET 9, there was no built-in way to specify custom enum names — you needed a custom JsonConverter or a third-party library like System.Text.Json.Serialization.JsonStringEnumMemberConverter.

Default Behavior

csharp
1using System.Text.Json;
2
3public enum Status
4{
5    Active,
6    Inactive,
7    PendingReview
8}
9
10var json = JsonSerializer.Serialize(Status.PendingReview);
11Console.WriteLine(json);  // 2 (integer by default)

Without any configuration, enums serialize as their integer values. This is compact but not human-readable and breaks if enum values are reordered.

String Serialization with JsonStringEnumConverter

csharp
1using System.Text.Json;
2using System.Text.Json.Serialization;
3
4var options = new JsonSerializerOptions
5{
6    Converters = { new JsonStringEnumConverter() }
7};
8
9var json = JsonSerializer.Serialize(Status.PendingReview, options);
10Console.WriteLine(json);  // "PendingReview"
11
12// Deserialization
13var status = JsonSerializer.Deserialize<Status>("\"PendingReview\"", options);
14Console.WriteLine(status);  // PendingReview
15
16// Case-insensitive by default
17var status2 = JsonSerializer.Deserialize<Status>("\"pendingReview\"", options);
18Console.WriteLine(status2);  // PendingReview

JsonStringEnumConverter serializes enum values as their C# member names. It supports case-insensitive deserialization by default.

Custom Names with JsonStringEnumMemberName (.NET 9+)

csharp
1using System.Text.Json;
2using System.Text.Json.Serialization;
3
4public enum OrderStatus
5{
6    [JsonStringEnumMemberName("new")]
7    New,
8
9    [JsonStringEnumMemberName("in-progress")]
10    InProgress,
11
12    [JsonStringEnumMemberName("shipped")]
13    Shipped,
14
15    [JsonStringEnumMemberName("cancelled")]
16    Cancelled
17}
18
19var options = new JsonSerializerOptions
20{
21    Converters = { new JsonStringEnumConverter() }
22};
23
24var json = JsonSerializer.Serialize(OrderStatus.InProgress, options);
25Console.WriteLine(json);  // "in-progress"
26
27var status = JsonSerializer.Deserialize<OrderStatus>("\"in-progress\"", options);
28Console.WriteLine(status);  // InProgress

JsonStringEnumMemberName is the built-in solution in .NET 9. It lets you specify the exact JSON string for each enum value, including lowercase, kebab-case, or any custom format.

Custom Names Before .NET 9 (EnumMember + Custom Converter)

csharp
1using System.Runtime.Serialization;
2using System.Text.Json;
3using System.Text.Json.Serialization;
4using System.Reflection;
5
6public enum Priority
7{
8    [EnumMember(Value = "low")]
9    Low,
10
11    [EnumMember(Value = "medium")]
12    Medium,
13
14    [EnumMember(Value = "high")]
15    High,
16
17    [EnumMember(Value = "critical")]
18    Critical
19}
20
21public class EnumMemberConverter<T> : JsonConverter<T> where T : struct, Enum
22{
23    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
24    {
25        var value = reader.GetString();
26        foreach (var field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static))
27        {
28            var attr = field.GetCustomAttribute<EnumMemberAttribute>();
29            if (attr?.Value == value || field.Name == value)
30                return (T)field.GetValue(null)!;
31        }
32        throw new JsonException($"Unknown value: {value}");
33    }
34
35    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
36    {
37        var field = typeof(T).GetField(value.ToString()!);
38        var attr = field?.GetCustomAttribute<EnumMemberAttribute>();
39        writer.WriteStringValue(attr?.Value ?? value.ToString());
40    }
41}
42
43// Usage
44var options = new JsonSerializerOptions
45{
46    Converters = { new EnumMemberConverter<Priority>() }
47};
48
49var json = JsonSerializer.Serialize(Priority.Critical, options);
50Console.WriteLine(json);  // "critical"

Before .NET 9, use [EnumMember(Value = "...")] from System.Runtime.Serialization combined with a custom converter that reads the attribute.

Naming Policy (camelCase, snake_case)

csharp
1using System.Text.Json;
2using System.Text.Json.Serialization;
3
4public enum Color
5{
6    DarkRed,
7    LightBlue,
8    ForestGreen
9}
10
11// camelCase naming policy
12var options = new JsonSerializerOptions
13{
14    Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
15};
16
17Console.WriteLine(JsonSerializer.Serialize(Color.DarkRed, options));
18// "darkRed"
19
20// Snake case (.NET 8+)
21var snakeOptions = new JsonSerializerOptions
22{
23    Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) }
24};
25
26Console.WriteLine(JsonSerializer.Serialize(Color.LightBlue, snakeOptions));
27// "light_blue"

JsonStringEnumConverter accepts a JsonNamingPolicy to transform all enum names. .NET 8 added SnakeCaseLower, SnakeCaseUpper, KebabCaseLower, and KebabCaseUpper policies.

Applying per Enum Type

csharp
1// Apply converter to a specific property
2public class Order
3{
4    public int Id { get; set; }
5
6    [JsonConverter(typeof(JsonStringEnumConverter))]
7    public OrderStatus Status { get; set; }
8
9    // This enum serializes as integer (no converter)
10    public Priority Priority { get; set; }
11}
12
13var order = new Order { Id = 1, Status = OrderStatus.Shipped, Priority = Priority.High };
14var json = JsonSerializer.Serialize(order);
15// {"Id":1,"Status":"Shipped","Priority":2}

Apply [JsonConverter] to individual properties to control serialization per field. Properties without the attribute use the default (integer) serialization.

Flags Enums

csharp
1[Flags]
2public enum Permissions
3{
4    None = 0,
5    Read = 1,
6    Write = 2,
7    Execute = 4,
8    All = Read | Write | Execute
9}
10
11var options = new JsonSerializerOptions
12{
13    Converters = { new JsonStringEnumConverter() }
14};
15
16var json = JsonSerializer.Serialize(Permissions.Read | Permissions.Write, options);
17Console.WriteLine(json);  // "Read, Write"
18
19var perms = JsonSerializer.Deserialize<Permissions>("\"Read, Write\"", options);
20Console.WriteLine(perms);  // Read, Write

JsonStringEnumConverter handles [Flags] enums by joining member names with commas. Deserialization parses the comma-separated string back into the combined flags value.

Common Pitfalls

  • No built-in custom names before .NET 9: JsonStringEnumMemberName was added in .NET 9. For earlier versions, you need a custom converter or the NuGet package Macross.Json.Extensions which provides [JsonStringEnumMemberName] compatibility.
  • JsonStringEnumConverter is case-insensitive by default: Deserialization matches "active", "Active", and "ACTIVE" to the same enum value. If you need strict casing, pass allowIntegerValues: false and validate manually.
  • Integer fallback on unknown strings: If deserialization encounters an unknown string, JsonStringEnumConverter throws JsonException. It does not silently default to 0 or the first enum value. Handle this with try-catch or input validation.
  • Naming policy overrides custom names: If you set both JsonNamingPolicy.CamelCase and [JsonStringEnumMemberName("custom")], the attribute takes precedence over the naming policy in .NET 9+.
  • Flags enum with unnamed combinations: Permissions.Read | Permissions.Execute serializes as "Read, Execute". But the value 3 (Read | Write) requires both names to be defined — if the combination is unnamed, it serializes as the integer 3 unless all individual flags are defined.

Summary

  • Use JsonStringEnumConverter to serialize enums as strings instead of integers
  • Use [JsonStringEnumMemberName("name")] in .NET 9+ for per-value custom names
  • Before .NET 9, use [EnumMember(Value = "name")] with a custom converter
  • Use JsonNamingPolicy.CamelCase or .SnakeCaseLower for automatic name transformation
  • Apply [JsonConverter] per property to mix string and integer enum serialization
  • [Flags] enums serialize as comma-separated member names

Course illustration
Course illustration

All Rights Reserved.