How to Tell JSON.Net Globally to Apply the Stringenumconverter to All Enums

How to tell Json.Net globally to apply the StringEnumConverter to all enums

Add a StringEnumConverter to the JsonSerializerSettings Converters collection.

Documentation: Serialize with JsonConverters


If you want the serializer to use camelCasing, you can set this as well:

SerializerSettings.Converters.Add(
new StringEnumConverter { CamelCaseText = true });

This will serialize SomeValue to someValue.

StringEnumConverter works as an attribute but not globally

In netcore 3.1 or higher, you can use JsonStringEnumConverter:

 var options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() },
//other options
};

Try Example online

How to tell JSON.NET StringEnumConverter to take DisplayName?

You should try using [EnumMember] instead of [Display]. You can also put the [JsonConverter] attribute on the enum itself.

[JsonConverter(typeof(StringEnumConverter))]
public enum Status
{
[EnumMember(Value = "Awaiting Approval")]
AwaitingApproval,
Rejected,
Accepted,
}

The VB.NET version for the JsonConverter attribute is:

<Newtonsoft.Json.JsonConverter(GetType(Newtonsoft.Json.Converters.StringEnumConverter))>

Override global Json.NET enum handling for one particular enum via decoration

Your main difficulty appears to be that you are not decorating your flag enums with FlagsAttribute, like so:

[Flags]
public enum Hobbies
{
Walking = 0x01,
Biking = 0x02,
// Etc
}

This is the recommended best practice for flag enums:

Designing Flag Enums

√ DO apply the System.FlagsAttribute to flag enums. Do not apply this attribute to simple enums.

See also here. If you don't do this, many enum-related .Net utilities may not work as expected for flag enumerations.

Having done this, StringEnumConverter will serialize flag enums with composite values as a set of comma-separated values instead of as the numeric value you are currently seeing:

{
"Hobbies": "walking, biking"
}

If you don't want this and still prefer to see default, numeric values for flag enums in your JSON, you can subclass StringEnumConverter to only convert non-flag enums:

public class NonFlagStringEnumConverter : StringEnumConverter
{
public override bool CanConvert(Type objectType)
{
if (!base.CanConvert(objectType))
return false;
return !HasFlagsAttribute(objectType);
}

static bool HasFlagsAttribute(Type objectType)
{
return Attribute.IsDefined(Nullable.GetUnderlyingType(objectType) ?? objectType, typeof(System.FlagsAttribute));
}
}

Then use it like:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new NonFlagStringEnumConverter  { CamelCaseText = true });

This will cause Json.NET to fall back on any global default JSON converter for enums, or to numeric serialization if there is no applicable fallback. Demo fiddle #1 here.

Additionally, if you need to supersede a converter applied at a higher level and force numeric serialization for flag enums, use the following:

public class ForceNumericFlagEnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (!(Nullable.GetUnderlyingType(objectType) ?? objectType).IsEnum)
return false;
return HasFlagsAttribute(objectType);
}

public override bool CanRead { get { return false; } }

public override bool CanWrite { get { return false; } }

static bool HasFlagsAttribute(Type objectType)
{
return Attribute.IsDefined(Nullable.GetUnderlyingType(objectType) ?? objectType, typeof(System.FlagsAttribute));
}

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();
}
}

Demo fiddle #2 here.

JavaScriptSerializer - JSON serialization of enum as string

No there is no special attribute you can use. JavaScriptSerializer serializes enums to their numeric values and not their string representation. You would need to use custom serialization to serialize the enum as its name instead of numeric value.


If you can use JSON.Net instead of JavaScriptSerializer than see answer on this question provided by OmerBakhari: JSON.net covers this use case (via the attribute [JsonConverter(typeof(StringEnumConverter))]) and many others not handled by the built in .net serializers. Here is a link comparing features and functionalities of the serializers.

Json.NET StringEnumConverter not always working

Thanks for everyone's help! I realized what I've done. Sadly it's pretty dumb, I apologize in advanced for the run around.

Since I am using GET I am sending the parameters as url query parameters so WebAPI is using the normal ModelBinder to map names and not JSON.NET. I'm not actually sending JSON so this make total sense. This question helped me realize this:
Complex type is getting null in a ApiController parameter

My choices are create a custom model binder that handles the enum correctly or change to a POST and send the data with JSON.stringify().

Json.NET StringEnumConverter not working as expected

The ItemConverterType property of the JsonPropertyAttribute attribute is the converter to use for items of a collection. You should be using the JsonConverterAttribute attribute.

public class TestData
{
[JsonConverter(typeof(StringEnumConverter))]
public TestEnum Enum { get; set; }
}

How to serialize enum as string globally (not by attribute in each enum)?

In .NET Core 3.0, the Newtonsoft.JSON package is no longer included by default.

Install the following package and try to add the converter like this:

services.AddMvc(...).AddNewtonsoftJson(opt => SerializerSettings.Converters.Add(new StringEnumConverter()));

deserialize json with array of enum

TL/DR: You have two basic problems here:

  1. .NET Core 3.0+ has a new built-in JSON serializer System.Text.Json, and you are mixing up attributes and classes between this new serializer and Json.NET. This is very easy to do when both are installed because they share some class names, such as JsonSerializer and JsonConverter.

  2. The new serializer is used by default but does not yet support serialization of enums as strings with custom value names; see System.Text.Json: How do I specify a custom name for an enum value? for details.

The easiest way to solve your problem is to switch back to Json.NET as shown here and use attributes, converters and namespaces exclusively from this serializer.

First let's break down the differences and similarities between the two serializers:

  1. System.Text.Json:

    • Built into .NET Core 3.0+ automatically and used for JSON serialization by ASP.NET Core 3.0+ by default.

    • Namespaces System.Text.Json and System.Text.Json.Serialization.

    • Classes including System.Text.Json.Serialization.JsonConverter, System.Text.Json.Serialization.JsonConverter<T> and System.Text.Json.JsonSerializer.

    • Attributes including System.Text.Json.Serialization.JsonPropertyNameAttribute, System.Text.Json.Serialization.JsonConverterAttribute and System.Text.Json.Serialization.JsonExtensionDataAttribute.

    • Serialization of enums as strings is supported by System.Text.Json.Serialization.JsonStringEnumConverter, however renaming via attributes is not implemented.

      See this answer to System.Text.Json: How do I specify a custom name for an enum value? for potential workarounds.

  2. Json.NET:

    • A 3rd-party library that can be used for serialization in ASP.NET Core 3.0+ by adding a NuGet reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson and then calling AddNewtonsoftJson() in Startup.ConfigureServices.

      For details see this answer to Where did IMvcBuilder AddJsonOptions go in .Net Core 3.0? by poke.

    • Namespaces including Newtonsoft.Json, Newtonsoft.Json.Converters, Newtonsoft.Json.Linq and Newtonsoft.Json.Serialization among others.

    • Classes including Newtonsoft.Json.JsonConverter, Newtonsoft.Json.JsonConverter<T> and Newtonsoft.Json.JsonSerializer

    • Attributes including Newtonsoft.Json.JsonPropertyAttribute, Newtonsoft.Json.JsonConverterAttribute and Newtonsoft.Json.JsonExtensionDataAttribute among others.

    • Serialization of enums as renamed strings is supported automatically by Newtonsoft.Json.Converters.StringEnumConverter when the EnumMemberAttribute attribute is applied.

With this in mind, which serializer are you using in your code? Since you helpfully included the namespaces in your question, we can check:

using System.Text.Json.Serialization; // System.Text.Json
using Newtonsoft.Json; // Json.NET

namespace Assignment_1
{
public class MyRequest
{
//...
[JsonProperty( // JsonProperty from Newtonsoft
"changeTypes",
ItemConverterType = typeof(JsonStringEnumConverter)// JsonStringEnumConverter from System.Text.Json
)]
public AppGlobals.BoardSymbols[] GameBoard { get; set; }
}
}

So as you can see, you are mixing up attributes from Newtonsoft with converters from System.Text.Json, which isn't going to work. (Perhaps you selected the namespaces from a "Resolve -> using ..." right-click in Visual Studio?)

So, how to resolve the problem? Since Json.NET supports renaming of enum values out of the box, the easiest way to resolve your problem is to use this serializer. While possibly not as performant as System.Text.Json it is much more complete and full-featured.

To do this, remove the namespaces System.Text.Json.Serialization and System.Text.Json and references to the type JsonStringEnumConverter from your code, and modify MyRequest and BoardSymbols as follows:

using System.Runtime.Serialization;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json;

namespace Assignment_1
{
public class MyRequest
{
//...
[Required]
[MinLength(9)]
[MaxLength(9)]
[JsonProperty("changeTypes")] // No need to add StringEnumConverter here since it's already applied to the enum itself
public AppGlobals.BoardSymbols[] GameBoard { get; set; }
}
}

namespace AppGlobals
{
[JsonConverter(typeof(StringEnumConverter))]
public enum BoardSymbols
{
[EnumMember(Value = "X")]
First = 'X',
[EnumMember(Value = "O")]
Second = 'O',
[EnumMember(Value = "?")]
EMPTY = '?'
}
}

Then NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson and in Startup.ConfigureServices call AddNewtonsoftJson():

services.AddMvc()
.AddNewtonsoftJson();

Or if you prefer to use StringEnumConverter globally:

services.AddMvc()
.AddNewtonsoftJson(o => o.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()));

Do take note of the following comment from the docs

Note: If the AddNewtonsoftJson method isn't available, make sure that you installed the Microsoft.AspNetCore.Mvc.NewtonsoftJson package. A common error is to install the Newtonsoft.Json package instead of the Microsoft.AspNetCore.Mvc.NewtonsoftJson package.

Mockup fiddle here.



Related Topics



Leave a reply



Submit