<PackageReference Include="NJsonSchema" Version="10.0.10" />

JsonInheritanceConverter

Defines the class as inheritance base class and adds a discriminator property to the serialized object.
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace NJsonSchema.Converters { public class JsonInheritanceConverter : JsonConverter { private readonly Type _baseType; private readonly string _discriminator; private readonly bool _readTypeProperty; [ThreadStatic] private static bool _isReading; [ThreadStatic] private static bool _isWriting; public static string DefaultDiscriminatorName { get; } = "discriminator"; public virtual string DiscriminatorName => _discriminator; public override bool CanWrite { get { if (_isWriting) { _isWriting = false; return false; } return true; } } public override bool CanRead { get { if (_isReading) { _isReading = false; return false; } return true; } } public JsonInheritanceConverter() : this(DefaultDiscriminatorName, false) { } public JsonInheritanceConverter(string discriminator) : this(discriminator, false) { } public JsonInheritanceConverter(string discriminator, bool readTypeProperty) { _discriminator = discriminator; _readTypeProperty = readTypeProperty; } public JsonInheritanceConverter(Type baseType) : this(baseType, DefaultDiscriminatorName) { } public JsonInheritanceConverter(Type baseType, string discriminator) : this(discriminator, false) { _baseType = baseType; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { try { _isWriting = true; JObject jObject = JObject.FromObject(value, serializer); jObject[_discriminator] = JToken.FromObject(GetDiscriminatorValue(value.GetType())); writer.WriteToken(jObject.CreateReader()); } finally { _isWriting = false; } } public override bool CanConvert(Type objectType) { if (_baseType != (Type)null) { Type type = objectType; while (type != (Type)null) { if (type == _baseType) return true; type = type.GetTypeInfo().BaseType; } return false; } return true; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jObject = serializer.Deserialize<JObject>(reader); if (jObject != null) { string discriminatorValue = jObject.GetValue(_discriminator).Value<string>(); Type discriminatorType = GetDiscriminatorType(jObject, objectType, discriminatorValue); try { _isReading = true; return serializer.Deserialize(jObject.CreateReader(), discriminatorType); } finally { _isReading = false; } } return null; } public virtual string GetDiscriminatorValue(Type type) { return type.Name; } protected virtual Type GetDiscriminatorType(JObject jObject, Type objectType, string discriminatorValue) { if (objectType.Name == discriminatorValue) return objectType; Type subtypeFromKnownTypeAttributes = GetSubtypeFromKnownTypeAttributes(objectType, discriminatorValue); if (subtypeFromKnownTypeAttributes != (Type)null) return subtypeFromKnownTypeAttributes; string name = objectType.Namespace + "." + discriminatorValue; Type type = objectType.GetTypeInfo().Assembly.GetType(name); if (type != (Type)null) return type; if (_readTypeProperty) { JToken value = jObject.GetValue("$type"); if (value != null) return Type.GetType(value.Value<string>()); } throw new InvalidOperationException("Could not find subtype of '" + objectType.Name + "' with discriminator '" + discriminatorValue + "'."); } private Type GetSubtypeFromKnownTypeAttributes(Type objectType, string discriminator) { Type type = objectType; do { foreach (dynamic item in from a in type.GetTypeInfo().GetCustomAttributes(false) where a.GetType().Name == "KnownTypeAttribute" select a) { dynamic val = item.Type != null; if ((val ? false : true) ? val : (val & (item.Type.Name == discriminator))) return (Type)item.Type; if (item.MethodName != null) { MethodInfo runtimeMethod = type.GetRuntimeMethod((string)item.MethodName, new Type[0]); if (runtimeMethod != (MethodInfo)null) { foreach (Type item2 in (IEnumerable<Type>)runtimeMethod.Invoke(null, new object[0])) { if (item2.Name == discriminator) return item2; } return null; } } } type = type.GetTypeInfo().BaseType; } while (type != (Type)null); return null; } } }