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

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 { internal static readonly string DefaultDiscriminatorName = "discriminator"; private readonly string _discriminator; private readonly bool _readTypeProperty; [ThreadStatic] private static bool _isReading; [ThreadStatic] private static bool _isWriting; 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 override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { try { _isWriting = true; JObject jObject = JObject.FromObject(value, serializer); jObject.AddFirst(new JProperty(_discriminator, value.GetType().get_Name())); writer.WriteToken(jObject.CreateReader()); } finally { _isWriting = false; } } public override bool CanConvert(Type objectType) { return true; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jObject = serializer.Deserialize<JObject>(reader); if (jObject != null) { string discriminator = jObject.GetValue(_discriminator).Value<string>(); Type objectSubtype = GetObjectSubtype(jObject, objectType, discriminator); try { _isReading = true; return serializer.Deserialize(jObject.CreateReader(), objectSubtype); } finally { _isReading = false; } } return null; } private Type GetObjectSubtype(JObject jObject, Type objectType, string discriminator) { if (objectType.get_Name() == discriminator) return objectType; Type subtypeFromKnownTypeAttributes = GetSubtypeFromKnownTypeAttributes(objectType, discriminator); if ((object)subtypeFromKnownTypeAttributes != null) return subtypeFromKnownTypeAttributes; string name = objectType.Namespace + "." + discriminator; Type type = objectType.GetTypeInfo().get_Assembly().GetType(name); if ((object)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.get_Name() + "' with discriminator '" + discriminator + "'."); } private Type GetSubtypeFromKnownTypeAttributes(Type objectType, string discriminator) { Type type = objectType; do { foreach (dynamic item in from a in CustomAttributeExtensions.GetCustomAttributes(type.GetTypeInfo(), false) where a.GetType().get_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 ((object)runtimeMethod != null) { foreach (Type item2 in (IEnumerable<Type>)runtimeMethod.Invoke(null, new object[0])) { if (item2.get_Name() == discriminator) return item2; } return null; } } } type = type.GetTypeInfo().get_BaseType(); } while ((object)type != null); return null; } } }