using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Newtonsoft.Json.Linq;
public class DictionaryWithNamingPolicyConverter : JsonConverterFactory
readonly JsonNamingPolicy namingPolicy;
public DictionaryWithNamingPolicyConverter(JsonNamingPolicy namingPolicy)
=> this.namingPolicy = namingPolicy ?? throw new ArgumentNullException();
public override bool CanConvert(Type typeToConvert)
if (typeToConvert.IsPrimitive || typeToConvert == typeof(string))
var parameters = typeToConvert.GetDictionaryKeyValueType();
return parameters != null && parameters[0] == typeof(string) && typeToConvert.GetConstructor(Type.EmptyTypes) != null;
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
var types = type.GetDictionaryKeyValueType();
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
typeof(DictionaryWithNamingPolicyConverterInner<,>).MakeGenericType(new Type[] { type, types[1] }),
BindingFlags.Instance | BindingFlags.Public,
args: new object[] { namingPolicy, options },
private class DictionaryWithNamingPolicyConverterInner<TDictionary, TValue> : JsonConverter<TDictionary>
where TDictionary : IDictionary<string, TValue>, new()
readonly JsonNamingPolicy namingPolicy;
readonly JsonConverter<TValue> _valueConverter;
readonly Type _valueType;
public DictionaryWithNamingPolicyConverterInner(JsonNamingPolicy namingPolicy, JsonSerializerOptions options)
this.namingPolicy = namingPolicy ?? throw new ArgumentNullException();
this._valueType = typeof(TValue);
this._valueConverter = (_valueType == typeof(object) ? null : (JsonConverter<TValue>)options.GetConverter(typeof(TValue)));
public override TDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
if (reader.TokenType == JsonTokenType.Null)
return default(TDictionary);
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException();
var dictionary = new TDictionary();
if (reader.TokenType == JsonTokenType.EndObject)
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException();
var key = reader.GetString();
dictionary.Add(key, _valueConverter.ReadOrDeserialize<TValue>(ref reader, _valueType, options));
throw new JsonException();
public override void Write(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOptions options)
writer.WriteStartObject();
foreach (var pair in dictionary)
writer.WritePropertyName(namingPolicy.ConvertName(pair.Key));
_valueConverter.WriteOrSerialize(writer, pair.Value, _valueType, options);
public static class JsonSerializerExtensions
public static void WriteOrSerialize<T>(this JsonConverter<T> converter, Utf8JsonWriter writer, T value, Type type, JsonSerializerOptions options)
converter.Write(writer, value, options);
JsonSerializer.Serialize(writer, value, type, options);
public static T ReadOrDeserialize<T>(this JsonConverter<T> converter, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> converter != null ? converter.Read(ref reader, typeToConvert, options) : (T)JsonSerializer.Deserialize(ref reader, typeToConvert, options);
public static class TypeExtensions
public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
=> (type ?? throw new ArgumentNullException()).IsInterface ? new[] { type }.Concat(type.GetInterfaces()) : type.GetInterfaces();
public static IEnumerable<Type []> GetDictionaryKeyValueTypes(this Type type)
=> type.GetInterfacesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)).Select(t => t.GetGenericArguments());
public static Type [] GetDictionaryKeyValueType(this Type type)
=> type.GetDictionaryKeyValueTypes().SingleOrDefaultIfMultiple();
public static class LinqExtensions
public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source)
var elements = source.Take(2).ToArray();
return (elements.Length == 1) ? elements[0] : default(TSource);
public static void Test()
static void TestOriginal()
var dictionary = new Dictionary<string, object>()
{"Key3", new Dictionary<string, object>(){ {"Key4", 123}}}
var options = new JsonSerializerOptions
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
Converters = { new DictionaryWithNamingPolicyConverter(JsonNamingPolicy.CamelCase) },
var serialized = JsonSerializer.Serialize(dictionary, options);
Console.WriteLine(serialized);
Assert.AreEqual(@"{""key1"":5,""key2"":""aaa"",""key3"":{""key4"":123}}", serialized);
public static void Test1()
var dictionary = new Dictionary<string, object>()
{"Key3", new Dictionary<string, object>(){ {"Key4", 123}}}
var options = new JsonSerializerOptions()
PropertyNamingPolicy = new CapitalizedNamingPolicy(),
DictionaryKeyPolicy = new CapitalizedNamingPolicy(),
Converters = { new DictionaryWithNamingPolicyConverter(JsonNamingPolicy.CamelCase) },
var serialized = JsonSerializer.Serialize(dictionary, options);
Console.WriteLine(serialized);
Assert.AreEqual(@"{""key1"":5,""key2"":""aaa"",""key3"":{""key4"":123}}", serialized);
var newDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(serialized, options);
var reserialized = JsonSerializer.Serialize(newDictionary, options);
Assert.AreEqual(Newtonsoft.Json.JsonConvert.SerializeObject(dictionary, new Newtonsoft.Json.JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }), reserialized);
public static void Test2()
var dictionary = new Dictionary<string, Dictionary<string, string>>()
{"Key1", new() { { "Key2", "Value2" } }},
var options = new JsonSerializerOptions()
PropertyNamingPolicy = new CapitalizedNamingPolicy(),
DictionaryKeyPolicy = new CapitalizedNamingPolicy(),
Converters = { new DictionaryWithNamingPolicyConverter(JsonNamingPolicy.CamelCase) },
var serialized = JsonSerializer.Serialize(dictionary, options);
Console.WriteLine(serialized);
Assert.AreEqual(@"{""key1"":{""key2"":""Value2""}}", serialized);
var newDictionary = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(serialized, options);
var reserialized = JsonSerializer.Serialize(newDictionary, options);
Assert.AreEqual(Newtonsoft.Json.JsonConvert.SerializeObject(dictionary, new Newtonsoft.Json.JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }), reserialized);
public class CapitalizedNamingPolicy : JsonNamingPolicy
public override string ConvertName(string name) => name.ToUpperInvariant();
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.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];