using System.Collections.Generic;
using Newtonsoft.Json.Serialization;
public static void Main()
var d = new Dictionary<B, B>()
string fileName = "lol.txt";
JsonSerializerSettings settings = new JsonSerializerSettings
Formatting = Formatting.Indented,
ContractResolver = new MyContractResolver()
JsonSerializer js = JsonSerializer.Create(settings);
js.Converters.Add(new MyDConverter<B, B, int>(new BKeyGetter(), new BObjGetter()));
Console.WriteLine("---- serialize ----");
using(var stream = File.Create(fileName))
using(var sw = new StreamWriter(stream))
var json = File.ReadAllText(fileName);
Console.WriteLine("---- deserialize ----");
using(var stream = File.OpenRead(fileName))
using(var sr = new StreamReader(stream))
d2 = (Dictionary<B, B>)js.Deserialize(sr, typeof(Dictionary<B, B>));
Console.WriteLine(d2[b].A.a);
Console.WriteLine(d2[b].A.b);
Console.WriteLine(d2[b].c);
public interface KeyGetter<in TKey>
public interface ObjGetter<out TKey>
TKey GetObject(string s);
public class AKeyGetter : KeyGetter<A>
const string Delimiter = "::";
public string GetKey(A a)
return string.Join(Delimiter, new string[]{a.a, a.b});
public class AObjGetter : ObjGetter<A>
const string Delimiter = "::";
public A GetObject(string s)
var split = s.Split(new string[]{Delimiter}, StringSplitOptions.None);
public class BKeyGetter : KeyGetter<B>
private static readonly AKeyGetter aKeyGetter = new AKeyGetter();
const string Delimiter = "::";
public string GetKey(B b)
return string.Join(Delimiter, new string[]{aKeyGetter.GetKey(b.A), b.c});
public class BObjGetter : ObjGetter<B>
private static readonly AObjGetter aObjGetter = new AObjGetter();
const string Delimiter = "::";
public B GetObject(string b)
var split = b.Split(new string[]{Delimiter}, StringSplitOptions.None);
var a = aObjGetter.GetObject(b);
internal readonly string a;
internal readonly string b;
public A(string a, string b)
internal readonly string c;
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
props.ForEach(p => { p.Writable = true; p.Readable = true; });
public class MyDConverter<TKey, TValue, TOrderingKey> : JsonConverter
private readonly Func<KeyValuePair<TKey, TValue>, TOrderingKey> orderByFunction;
private readonly KeyGetter<TKey> keyGetter;
private readonly ObjGetter<TKey> objGetter;
public readonly bool OrderByDescending;
private readonly bool ConvertIntsToInt32;
KeyGetter<TKey> keyGetter,
ObjGetter<TKey> objGetter,
Func<KeyValuePair<TKey, TValue>, TOrderingKey> orderByFunction = null,
bool orderByDescending = true,
bool convertIntsToInt32 = false)
var keyType = typeof(TKey);
throw new NotSupportedException("can't instantiate abstract class");
throw new NotSupportedException("can't instantiate interface");
this.keyGetter = keyGetter;
this.objGetter = objGetter;
this.orderByFunction = orderByFunction;
this.OrderByDescending = orderByDescending;
this.ConvertIntsToInt32 = convertIntsToInt32;
public override bool CanConvert(Type objectType)
if (typeof(IDictionary<TKey, TValue>).IsAssignableFrom(objectType))
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer js)
Dictionary<TKey, TValue> dict = (Dictionary<TKey, TValue>)existingValue ?? new Dictionary<TKey, TValue>();
string currentKeyAsString;
switch (reader.TokenType)
case JsonToken.PropertyName:
currentKeyAsString = (string)reader.Value;
key = this.objGetter.GetObject( currentKeyAsString );
if (reader.TokenType == JsonToken.StartObject)
value = js.Deserialize<TValue>(reader);
if (reader.TokenType == JsonToken.Integer)
if (this.ConvertIntsToInt32)
value = (TValue)Convert.ChangeType(Convert.ToInt32((Int64)reader.Value), typeof(TValue));
value = (TValue)reader.Value;
throw new NotImplementedException();
case JsonToken.EndObject:
throw new InvalidOperationException();
public override void WriteJson(JsonWriter jw, object value, JsonSerializer js)
var dict = (IDictionary<TKey, TValue>)value;
IEnumerable<KeyValuePair<TKey, TValue>> orderedKvps = null;
if (orderByFunction == null)
orderedKvps = dict.AsEnumerable();
orderedKvps = this.OrderByDescending ? dict.OrderByDescending(this.orderByFunction) : dict.OrderBy(this.orderByFunction);
foreach (var kvp in orderedKvps)
var keyAsString = this.keyGetter.GetKey(kvp.Key);
jw.WritePropertyName(keyAsString);
js.Serialize(jw, kvp.Value);