Skip to content

Instantly share code, notes, and snippets.

Created October 3, 2018 22:27
Show Gist options
  • Save luisgoncalves/5c6a5b39d80bf78155ae6a518dc60a24 to your computer and use it in GitHub Desktop.
Save luisgoncalves/5c6a5b39d80bf78155ae6a518dc60a24 to your computer and use it in GitHub Desktop.
A Json.NET converter that handles an hierarchy of types based on a custom discriminator property
class SubTypesConverter<T> : JsonConverter<T> where T: new()
private static bool isWriting;
private readonly string discriminatorName;
private readonly Dictionary<string, Func<T>> factories;
private readonly Dictionary<Type, string> discriminators;
public override bool CanRead => true;
public override bool CanWrite => !isWriting;
public SubTypesConverter(string discriminatorName)
this.discriminatorName = discriminatorName;
this.factories = new Dictionary<string, Func<T>>();
this.discriminators = new Dictionary<Type, string>();
var types = typeof(T).Assembly
.Where(t => typeof(T).IsAssignableFrom(t) && !t.IsAbstract);
foreach (var t in types)
var discriminator = this.GetDiscriminator(t);
this.factories.Add(discriminator, CreateFactory(t));
this.discriminators.Add(t, discriminator);
public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
if (hasExistingValue)
throw new NotSupportedException($"{nameof(SubTypesConverter<T>)} does not allow reading into an existing instance");
var jsonObject = JObject.Load(reader);
var discriminator = jsonObject[this.discriminatorName].Value<string>();
var value = this.factories[discriminator]();
serializer.Populate(jsonObject.CreateReader(), value);
return value;
public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
isWriting = true;
var jsonObject = JObject.FromObject(value, serializer);
jsonObject.AddFirst(new JProperty(this.discriminatorName, this.discriminators[value.GetType()]));
isWriting = false;
protected virtual string GetDiscriminator(Type type)
return type.Name;
private static Func<T> CreateFactory(Type t)
var newExp = Expression.New(t.GetConstructor(Type.EmptyTypes));
return Expression.Lambda<Func<T>>(newExp).Compile();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment