Last active
October 29, 2024 21:06
-
-
Save jtillman/93768c5f77014781b7b814cd582e8595 to your computer and use it in GitHub Desktop.
JsonConverter to setup a Polymorphic Use of Json Serialization
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
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