using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
[EnumMember(Value = "Trick-Or-Treat")]
public Example? Example { get; set; }
public class CustomJsonStringEnumConverter : JsonConverterFactory
private readonly JsonNamingPolicy namingPolicy;
private readonly bool allowIntegerValues;
private readonly JsonStringEnumConverter baseConverter;
public CustomJsonStringEnumConverter() : this(null, true) { }
public CustomJsonStringEnumConverter(JsonNamingPolicy namingPolicy = null, bool allowIntegerValues = true)
this.namingPolicy = namingPolicy;
this.allowIntegerValues = allowIntegerValues;
this.baseConverter = new JsonStringEnumConverter(namingPolicy, allowIntegerValues);
public override bool CanConvert(Type typeToConvert) => baseConverter.CanConvert(typeToConvert);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
var query = from field in typeToConvert.GetFields(BindingFlags.Public | BindingFlags.Static)
let attr = field.GetCustomAttribute<EnumMemberAttribute>()
select (field.Name, attr.Value);
var dictionary = query.ToDictionary(p => p.Item1, p => p.Item2);
if (dictionary.Count > 0)
return new JsonStringEnumConverter(new DictionaryLookupNamingPolicy(dictionary, namingPolicy), allowIntegerValues).CreateConverter(typeToConvert, options);
return baseConverter.CreateConverter(typeToConvert, options);
public class JsonNamingPolicyDecorator : JsonNamingPolicy
readonly JsonNamingPolicy underlyingNamingPolicy;
public JsonNamingPolicyDecorator(JsonNamingPolicy underlyingNamingPolicy) => this.underlyingNamingPolicy = underlyingNamingPolicy;
public override string ConvertName (string name) => underlyingNamingPolicy == null ? name : underlyingNamingPolicy.ConvertName(name);
internal class DictionaryLookupNamingPolicy : JsonNamingPolicyDecorator
readonly Dictionary<string, string> dictionary;
public DictionaryLookupNamingPolicy(Dictionary<string, string> dictionary, JsonNamingPolicy underlyingNamingPolicy) : base(underlyingNamingPolicy) => this.dictionary = dictionary ?? throw new ArgumentNullException();
public override string ConvertName (string name) => dictionary.TryGetValue(name, out var value) ? value : base.ConvertName(name);
public static void Test()
var values = Enum.GetValues(typeof(Example)).Cast<Example>().Concat(new [] { Example.Trick | Example.TrickOrTreat, Example.Treat | Example.TrickOrTreat, Example.Trick | Example.Treat | Example.TrickOrTreat }).ToArray();
Test(values, JsonNamingPolicy.CamelCase);
Test(values, new TestNamingPolicy());
TestSimple(JsonNamingPolicy.CamelCase);
TestSimple(new TestNamingPolicy());
Console.WriteLine("\nJson.NET output: ");
var settings = new Newtonsoft.Json.JsonSerializerSettings
Converters = { new Newtonsoft.Json.Converters.StringEnumConverter(new TestNamingStrategy()) },
Formatting = Newtonsoft.Json.Formatting.Indented,
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(values, settings));
static void TestSimple(JsonNamingPolicy namingPolicy)
var options = new JsonSerializerOptions
Converters = { new CustomJsonStringEnumConverter(namingPolicy) },
Assert.IsTrue(JsonSerializer.Serialize(Example.TrickOrTreat, options) == "\"Trick-Or-Treat\"");
var test = new RootObject { Example = Example.TrickOrTreat };
var json = JsonSerializer.Serialize(test, options);
Assert.IsTrue(json.Contains("Trick-Or-Treat"));
public static void Test<T>(T values, JsonNamingPolicy namingPolicy)
var options = new JsonSerializerOptions
Converters = { new CustomJsonStringEnumConverter(namingPolicy) },
var json = JsonSerializer.Serialize(values, options);
public class TestNamingStrategy : Newtonsoft.Json.Serialization.NamingStrategy
protected override string ResolvePropertyName(string name) => "enum_" + name;
public class TestNamingPolicy : JsonNamingPolicy
public override string ConvertName (string name) => "enum_" + name;
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];