using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
[EnumMember(Value = "Trick-Or-Treat")]
public Example? Example { get; set; }
public class JsonEnumMemberStringEnumConverter : JsonConverterFactory
private readonly JsonNamingPolicy? namingPolicy;
private readonly bool allowIntegerValues;
private readonly JsonStringEnumConverter baseConverter;
public JsonEnumMemberStringEnumConverter() : this(null, true) { }
public JsonEnumMemberStringEnumConverter(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>()
where attr != null && attr.Value != null
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?.ConvertName(name) ?? 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();
TestRoundTrip(values, null);
TestRoundTrip(values, JsonNamingPolicy.CamelCase);
TestRoundTrip(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 JsonEnumMemberStringEnumConverter(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 TestRoundTrip<T>(IEnumerable<T> values, JsonNamingPolicy? namingPolicy)
var options = new JsonSerializerOptions
Converters = { new JsonEnumMemberStringEnumConverter(namingPolicy) },
var json = JsonSerializer.Serialize(values, options);
var newValues = JsonSerializer.Deserialize<T []>(json, options);
Assert.That(values.SequenceEqual(newValues!));
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}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");