Created
October 3, 2018 22:27
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SubTypesConverter<T> : JsonConverter<T> where T: new() | |
{ | |
[ThreadStatic] | |
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 | |
.GetTypes() | |
.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) | |
{ | |
try | |
{ | |
isWriting = true; | |
var jsonObject = JObject.FromObject(value, serializer); | |
jsonObject.AddFirst(new JProperty(this.discriminatorName, this.discriminators[value.GetType()])); | |
jsonObject.WriteTo(writer); | |
} | |
finally | |
{ | |
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