In this article, we are going to learn how to serialize enum to a string in C#.

To download the source code for this article, you can visit our GitHub repository.

In C#, JSON serialization very often needs to deal with enum objects. By default, enums are serialized in their integer form. This often causes a lack of interoperability with consumer applications because they need prior knowledge of what those numbers actually mean. So, we want them to serialize as strings in many instances. We are going to see various ways to do this using the native System.Text.Json library and the popular Newtonsoft.Json library. It is a general-purpose solution for most parts. 

Default Way to Serialize Enum to String in C#

To begin, let’s prepare a few object models:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!
public class Canvas
{
    public static Canvas Poster 
        => new() { Name = "Poster", BackColor = Color.LightGray, Pen = new ("Simple", Color.Red) };

    public string? Name { get; set; }

    public Color BackColor { get; set; }

    public Medium Medium { get; set; }

    public Pen? Pen { get; set; }
}

public record struct Pen(string Name, Color Color);

public enum Color
{
    White, LightGray, DarkGray, Red
}

public enum Medium
{
    Water, Oil
}

We declare two enums: Color and Medium, a record Pen, and a class Canvas. Canvas is our primary model in concern. Also, we declare a static Canvas instance (Poster) for convenient use in examples.

Next, we are going to add a basic Serialize method in the base class (UnitTestBase):

// Native
public static string Serialize(object obj)
{
    return JsonSerializer.Serialize(obj);
}

// Newtonsoft
public static string Serialize(object obj)
{
    return JsonConvert.SerializeObject(obj);
}

All set, we’re ready to go.

First, let’s check the default behavior of serialization on Canvas.Poster object:

var json = Serialize(Canvas.Poster);

And let’s inspect the result:

{
  "Name": "Poster",
  "BackColor": 1,
  "Medium": 0,
  "Pen": {
    "Name": "Simple",
    "Color": 3
  }
}

No wonder, the resulting string contains the enum properties (BackColor, Medium, Pen.Color) as integer values.

So, the question arises: “Can enum be serialized to a string in C#”? Let’s look for the answer in the rest of the article.

Different Ways to Serialize Enum to String in C#

We want to serialize the enums as strings. Both native and Newtonsoft libraries provide a converter for this purpose, named as JsonStringEnumConverter and StringEnumConverter respectively. They also provide an attribute JsonConverterAttribute that we can use to serialize string-enum selectively.

Serialization of an Enum Property

First of all, we want to serialize the BackColor property as a string. So, it’s time to change the Canvas model and decorate the BackColor property with the converter attribute:

// Native
public class Canvas
{
    ...
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public Color BackColor { get; set; }
    ...
}

// Newtonsoft
public class Canvas
{
    ...
    [JsonConverter(typeof(StringEnumConverter))]
    public Color BackColor { get; set; }
    ...
}

Now the serialization of Canvas.Poster :

var json = Serialize(Canvas.Poster);

Produces a different output:

{
  "Name": "Poster",
  "BackColor": "LightGray",
  "Medium": 0,
  "Pen": {
    "Name": "Simple",
    "Color": 3
  }
}

We can see that BackColor turns to string but Medium and Pen.Color don’t, because they don’t have the converter attribute applied. This means we can selectively serialize specific enum properties in this way.

Serialization of an Enum Type

Next, we want to serialize all instances of the Color enum to strings. We can do this by applying the converter attribute on the enumeration type itself instead of the properties:

public class Canvas
{
    ...
    public Color BackColor { get; set; }
    ...
}

// Native
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Color
{
    White, LightGray, DarkGray, Red
}

// Newtonsoft
[JsonConverter(typeof(StringEnumConverter))]
public enum Color
{
    White, LightGray, DarkGray, Red
}

Again, after the serialization, the end result differs from the previous one:

{
  "Name": "Poster",
  "BackColor": "LightGray",
  "Medium": 0,
  "Pen": {
    "Name": "Simple",
    "Color": "Red"
  }
}

This time we can see all Color instances (BackColor and Pen.Color) turn to string, but Medium enum still follows the default behavior. So in this way, we can selectively serialize some specific enum types to string.

Inline JSON Serialization of Enum to String

We can’t go with the attribute-based approaches in some cases such as:

  • Dealing with system types or third party library models
  • Serialize objects generated on the fly e.g. anonymous objects
  • Don’t want to pollute our domain models but still want to get enum as a string
  • Stringify enums only for a particular object instance, not all instances in general

In such cases, if we don’t bother about selective serialization, we can instruct the serializer to convert enum on-demand. Both libraries offer an overload to pass converters in line with the serialization method. So, let’s implement our second serialization routine in the base class:

// Native
public static string SerializeWithStringEnum(object obj)
{
    var options = new JsonSerializerOptions();
    options.Converters.Add(new JsonStringEnumConverter());

    return JsonSerializer.Serialize(obj, options);
}

// Newtonsoft
public static string SerializeWithStringEnum(object obj)
{
    var converter = new StringEnumConverter();
    return JsonConvert.SerializeObject(obj, converter);
}

In the case of the native version, we instantiate a JsonSerializerOptions class. Then we register the enum converter there and finally call the appropriate Serialize method. 

Things are a bit straightforward for Newtonsoft. We can directly pass the converter to the serializing method.

Next, we are going to get rid of the converter attributes from all our models and instead apply this new method on Canvas.Poster and an anonymous object:

var poster = SerializeWithStringEnum(Canvas.Poster);
var schedule = SerializeWithStringEnum(new { Description = "Exhibition", Day = DayOfWeek.Monday });

Now, we can inspect the result:

/* poster */
{
  "Name": "Poster",
  "BackColor": "LightGray",
  "Medium": "Water",
  "Pen": {
    "Name": "Simple",
    "Color": "Red"
  }
}
/* schedule */
{
  "Description": "Exhibition",
  "Day": "Monday"
}

As we expect, all enums of the target objects are serialized in a string form.

Global JSON Serialization of Enum to String

Attribute-based ways give us the flexibility to manipulate the enum serialization in a controlled way. And the options-based way allows us to deal with specific object instances on demand. But, is there any way to make all enums serialized as strings by default? Yes, there is! 

To make the string-enum converter default choice for enum serialization, we need some bootstrapping that varies depending on the application types. Typically this means, we have to register the enum converter in the DI pipeline. Here, we will focus on ASP.NET Core applications mainly.

Configure ASP.NET Core Web API

We are going to start with a basic ASP.NET Core Web API project and remove all auto-generated controllers and models. Next, let’s add our object models as usual and a CanvasController:

[ApiController]
[Route("[controller]")]
public class CanvasController : ControllerBase
{
    [HttpGet("poster")]
    public Canvas GetPoster() => Canvas.Poster;

    [HttpGet("schedule")]
    public object GetSchedule() => new { Description = "Exhibition", Day = DayOfWeek.Monday };
}

This is a typical Web API controller with two simple endpoints: “canvas/poster” and “canvas/schedule”. They provide output for Canvas.Poster and an anonymous object respectively.

Now, let’s move to the entry point, the Program class:

// Native
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
    });

var app = builder.Build();

app.UseAuthorization();

app.MapControllers();

app.Run();

The highlighted part is all that we need to change in this boilerplate code. We just get into the DI pipeline using the AddJsonOptions method and get access to the JsonSerializerOptions configuration. This is where we can register the native string-enum converter. That’s all! Every time the framework attempts to serialize enum to JSON, it will pick this converter from the DI pipeline.

We can do this for Newtonsoft in a similar fashion:

...
builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.Converters.Add(new StringEnumConverter());
    });
...

Again, all we need is to find the appropriate DI configurator method to use the Newtonsoft serializer. For this, we have to install Microsoft.AspNetCore.Mvc.NewtonsoftJson package. This provides the AddNewtonsoftJson configurator method. It exposes the serializer settings where we can register the Newtonsoft enum converter.

Now, we can examine the output of our API endpoints from Postman or directly from a browser:

/* http://localhost:5045/canvas/poster */
{
  "name": "Poster",
  "backColor": "LightGray",
  "medium": "Water",
  "pen": {
    "name": "Simple",
    "color": "Red"
  }
}
/* http://localhost:5045/canvas/schedule */
{
  "description": "Exhibition",
  "day": "Monday"
}

We get exactly what we desire, all string-enums by default!

Configure ASP.NET Core Minimal API

Next, we are going to explore the configuration for the cutting-edge .NET technology: Minimal API. This is Web API at its core, but the bootstrapping steps are a bit different. Also, Minimal API does not yet support configuring Newtonsoft out of the box. So, we are going to discuss only System.Text.Json library.

Let’s modify our auto-generated Program class and add the bootstrapping code:

var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

var app = builder.Build();

app.MapGet("/poster", () => Canvas.Poster);
app.MapGet("/schedule", () => new { Description = "Exhibition", Day = DayOfWeek.Monday });

app.Run();

This time, we configure the JsonOptions to register the string-enum converter. In addition, we map two routes: poster and schedule. These endpoints work the same as our previous controller examples and the output is also identical.

As we can see, we can set up our application for serializing enums as strings by default without any extra model decoration! In this way, we can work with clean models. Also, we don’t need to worry about specifying explicit options every time we serialize an object. 

Serialization of Enum to a Customized String

While serializing enum to a string, we may want some fine-tuning. For example, transform to camelCase or expose as a more meaningful text, etc. Let’s explore how we can do these.

Serialize to a camelCase Text

JSON serialization to a camelCase string is a common practice. This is in fact the default behavior for ASP.NET Core. But in general, this means camelCase transformation of property names, not their values:

// Native
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
options.Converters.Add(new JsonStringEnumConverter());

var json = JsonSerializer.Serialize(Canvas.Poster, options);
{
  "name": "Poster",
  "backColor": "LightGray",
  "medium": "Water",
  "pen": {
    "name": "Simple",
    "color": "Red"
  }
}

This is self-explanatory and technically logical but may not be desirable for enums in practice. That’s why the enum converter offers a way to explicitly transform enum values to camelCase:

// Native
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));

var json = JsonSerializer.Serialize(Canvas.Poster, options);

// Newtonsoft
var converter = new StringEnumConverter(new CamelCaseNamingStrategy());
var json = JsonConvert.SerializeObject(Canvas.Poster, converter);

We just need to specify the camelCase policy (or strategy) during the enum converter instantiation and pass it during serialization as usual:

{
  "Name": "Poster",
  "BackColor": "lightGray",
  "Medium": "water",
  "Pen": {
    "Name": "Simple",
    "Color": "red"
  }
}

All the enum values are now in camelCase format.

Serialize as Custom Text

Because of language’s variable naming rules, an enum member can’t always convey meaningful text when serialized in a regular way. To get around this problem, we generally use EnumMemberAttribute from System.Runtime.Serialization assembly that is specifically introduced for this purpose:

public record struct ToggleControl(string Name, ToggleType Type);

public enum ToggleType
{
    [EnumMember(Value = "Enable/Disable")]
    EnableDisable,

    [EnumMember(Value = "Visible/Hidden")]
    VisibleHidden,

    [EnumMember(Value = "Editable/Readonly")]
    EditableReadonly,
}

We define a ToggleType enum that has EnumMemberAttribute on its members to provide more meaningful values than their names. We also define a ToggleControl record that uses this enum.

That’s it, we’re ready to go.

Sadly, the native library doesn’t support EnumMemberAttribute yet, but Newtonsoft does. We can implement a custom converter extending from the native JsonStringEnumConverter and add this functionality on our own. But, that is another level of job that we will try to cover in a future post. For now, let’s just stick with Newtonsoft:

// Newtonsoft
var controls = new ToggleControl[]
{ 
    new("toggle1", ToggleType.EnableDisable),
    new("toggle2", ToggleType.VisibleHidden)
};
var json = SerializeWithStringEnum(controls);
[
  {
    "Name": "toggle1",
    "Type": "Enable/Disable"
  },
  {
    "Name": "toggle2",
    "Type": "Visible/Hidden"
  }
]

We create an array of ToggleControl with different ToggleType values. On serialization, we can see it produces output with our desired texts!

JSON Serialization of Flag enums

Default serialization of flag enums (bit-mask enum) produces even more odd output. Instead of representing as a combination of flags, they’re exposed as a combined value:

[Flags]
public enum TextStyles
{
    None = 0,
    Bold = 1,
    Italic = 2,
    Underline = 4,
}

var styles = TextStyles.Bold | TextStyles.Italic | TextStyles.Underline;
var json = Serialize(new { Format = styles });

The output:

{"Format":7}

We start with defining a TextStyles flag. Then we declare a styles which is a combination of Bold(1), Italic(2), Underline(4) flags. For default serialization, we may expect it to serialize as “1, 2, 4” at least. But, we get a 7 instead because the default serializer just cares about the numeric value for enum.

The string-enum conversion is free from this oddness:

var styles = TextStyles.Bold | TextStyles.Italic | TextStyles.Underline;
var json = SerializeWithStringEnum(new { Format = styles });

Now we have a different result:

{"Format":"Bold, Italic, Underline"}

That’s the output we desire!

Conclusion

In this article, we have learned a few ways of JSON serialization of enum as a string. We have also discussed various techniques to get customized string serialization of an enum.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!