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

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.Linq; using System.Reflection; namespace NJsonSchema.Converters { public class JsonInheritanceConverter : JsonConverter { internal static readonly string DefaultDiscriminatorName = "discriminator"; private readonly string _discriminator; [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() { _discriminator = DefaultDiscriminatorName; } public JsonInheritanceConverter(string discriminator) { _discriminator = discriminator; } 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().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); 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; } } private Type GetObjectSubtype(JObject jObject, Type objectType, string discriminator) { dynamic val = (from a in objectType.GetTypeInfo().GetCustomAttributes() where a.GetType().Name == "KnownTypeAttribute" select a).SingleOrDefault((Attribute a) => IsKnwonTypeTargetType(a, discriminator)); if (val != null) return (Type)val.Type; string name = objectType.Namespace + "." + discriminator; Type type = objectType.GetTypeInfo().Assembly.GetType(name); if (type != (Type)null) return type; 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 '" + discriminator + "'."); } private bool IsKnwonTypeTargetType(dynamic attribute, string discriminator) { return (byte)(attribute?.Type.Name == discriminator) != 0; } } }