Now the title of this post is probably a bit of a mouthful and maybe isn’t what you think. This isn’t about overriding the default settings of JSON.NET, it’s about overriding JSON.NET *back* to default. That probably doesn’t make a heck of a lot of sense, but hopefully by the end of this post it will!
Returning Enums as Strings In ASP.NET Core
If you are returning an enum from an ASP.NET Core Web project, by default it’s going to return as the integer representation.
So for example if we have a model that contains an enum that looks like this :
public enum MyEnum { EnumValue1 = 1, EnumValue2 = 2 } public class MyModel { public MyEnum MyEnum { get; set; } }
This model will (by default) be serialized looking like so :
{"myEnum":1}
Which makes sense. It’s better that we return the integer values in most cases because these become the immutable “key” values, whereas the name of the actual enum can change.
However in some cases, we may want to return a string value. This could be because you have a pesky Javascript/IOS developer who wants string values and won’t budge, or maybe you just prefer it that way! In either case, there is a way to override the model serialization on a per class basis.
All we have to do is decorate our property with a special attribute.
public class MyModel { [JsonConverter(typeof(StringEnumConverter))] public MyEnum MyEnum { get; set; } }
You will require two using statements to go along with this one :
using Newtonsoft.Json; using Newtonsoft.Json.Converters;
What this does is tell JSON.NET (Which as of writing, is the default JSON serializer of .NET Core), to serialize this particular property using the StringEnumConverter. Which among other things, can just use the string representation of an enum. If we serialize this model now, we get :
{"myEnum":"EnumValue1"}
But the problem we now have is that we have to go and edit every single property that uses an enum and tell it to use the string representation. If we know we want it in every case across the board, then we can actually tell JSON.NET to use the StringEnumConverter everytime.
Head over to our startup.cs and find the ConfigureServices method. In there we should already see a call to AddMvc(), and we are just going to tack onto the end of this.
public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.Converters.Add(new StringEnumConverter()); } ); }
Here we are basically saying that here’s a converter you can use anytime it’s applicable (Which is basically for every Enum), so go ahead and use it.
The Problem (And The Fix)
If we have been humming along serializing enums as strings, we might end up in a position where we actually do want to return an enum as an integer. A prime example of this (At least it was for me), is returning error codes. I want the codes to be represented in C# as enums as it’s much easier to wrangle, but be serialized out as simple numeric code.
So what we now want to do is override our “new default” of StringEnumConverter, and go back to the old way of doing things. So how do we do that? Well actually we can’t. As crazy as that probably sounds, I couldn’t find any way to say “Please use the default converter instead of that other one I gave you”. Hellspin
Thinking that I was going to have to write my own custom converter just to cast back to an int, I came across an interesting little piece of documentation. It came of the form of the “CanWrite” property of a custom converter. The documentation of which is here : https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonConverter_CanWrite.htm
Note that if this is set to false, it’s saying that the JsonConverter cannot write the JSON. So what is it going to fall back to? As it turns out, the default converter for that type.
So all we need to do is whip up a custom JSON converter that does absolutely nothing :
public class ForceDefaultConverter : JsonConverter { public override bool CanRead => false; public override bool CanWrite => false; public override bool CanConvert(Type objectType) { throw new NotImplementedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
The NotImplemented methods are ones that I must have when I inherit from the base class. But because I set CanRead and CanWrite to false, they are never actually called.
Now if we decorate our enum property with this converter (Essentially forcing it to use no other custom converters even if they are in the setup list in our startup.cs)
public class MyModel { [JsonConverter(typeof(ForceDefaultConverter))] public MyEnum MyEnum { get; set; } }
Serializing this model again we get :
{"myEnum":1}
Perfect!
Thanks you really much for this! I couldn’t find a way to force the default converter for some specific enums while still wanting to have the StringEnumConverter in most cases!
This is an unbelievable useful and potent atricle!
I’m in th exact situation that you’ve described and I believe it to be pretty common.
I’m making a REST Client library for a DSS (Digital Signature Service) REST API.
The API has most of the enums passed as text, but few (more low-level, controlling tiniest bits) passed as integers.
Of course I modeled every enum as proper enum and added StringEnumConverter to the list of converters used by default and I was searching for the exact recipe that you’ve described.
Thank you very very much indeed!
I really wish there was an article for this on the System.Text.Json way of doing it. Looks like you have to implement the JsonConverterFactory to handle enum types and it’s more complicated.