Skip to content

Instantly share code, notes, and snippets.

@jtillman
Last active March 1, 2018 13:41
Show Gist options
  • Save jtillman/93768c5f77014781b7b814cd582e8595 to your computer and use it in GitHub Desktop.
Save jtillman/93768c5f77014781b7b814cd582e8595 to your computer and use it in GitHub Desktop.
JsonConverter to setup a Polymorphic Use of Json Serialization
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace polymorphic
{
[JsonConverter(typeof(AbstractClassJsonConverter), nameof(Type))]
public abstract class Transportation
{
[JsonProperty("type")]
public abstract string Type { get; }
[JsonProperty("speed")]
public string Speed { get; set; }
}
public class Bike : Transportation
{
public override string Type { get { return "bike"; } }
[JsonProperty("has_training_wheels")]
public bool HasTrainingWheels { get; set; }
}
public class Car : Transportation
{
public override string Type { get { return "car"; } }
[JsonPorperty("make")]
public string Make { get; set; }
[JsonProperty("model")]
public string Model { get; set; }
}
public class AbstractClassJsonConverter : JsonConverter
{
public string PropertyName { get; }
public AbstractClassJsonConverter(string propertyName)
{
PropertyName = propertyName;
}
private Dictionary<object, Type> _knownTypes;
public Dictionary<object, Type> GetKnownTypes(Type abstractType)
{
if (null == _knownTypes)
{
var knownTypes = new Dictionary<object, Type>();
foreach (var type in Assembly.GetAssembly(abstractType).GetTypes())
{
if (type == abstractType || type.IsAbstract || !type.IsSubclassOf(abstractType))
continue;
var typeGetMethod = type.GetProperty(PropertyName).GetGetMethod();
var typeClassifier = typeGetMethod.Invoke(Activator.CreateInstance(type), new object[0]);
if (knownTypes.ContainsKey(typeClassifier))
throw new ArgumentException("Duplicate Classes claiming type");
knownTypes[typeClassifier] = type;
}
_knownTypes = knownTypes;
}
return _knownTypes;
}
public override bool CanConvert(Type objectType)
{
return objectType.GetCustomAttributes(typeof(JsonConverterAttribute), true)
.Any(x => (x as JsonConverterAttribute).ConverterType == objectType);
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var knownType = GetKnownTypes(objectType);
var jObject = JObject.Load(reader);
var typeProperty = objectType.GetProperty(PropertyName);
var jsonName = typeProperty.GetCustomAttribute<JsonPropertyAttribute>(true).PropertyName;
var typeKey = jObject[jsonName].ToObject(typeProperty.PropertyType);
if (!knownType.ContainsKey(typeKey))
{
throw new InvalidDataException("Unkown Type");
}
var obj = Activator.CreateInstance(knownType[typeKey]);
serializer.Populate(jObject.CreateReader(), obj);
return obj;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
return;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment