In this article, we are going to learn how to serialize enum to a string in C#.
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:
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.