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 NJsonSchema.Infrastructure;
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 Type _baseType;
private readonly string _discriminator;
private readonly bool _readTypeProperty;
[ThreadStatic]
private static bool _isReading;
[ThreadStatic]
private static bool _isWriting;
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.AddFirst(new JProperty(_discriminator, 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 = ReflectionExtensions.GetTypeInfo(type).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 = ReflectionExtensions.GetTypeInfo(objectType).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 ReflectionExtensions.GetTypeInfo(type).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 = ReflectionExtensions.GetRuntimeMethod(type, (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 = ReflectionExtensions.GetTypeInfo(type).BaseType;
} while (type != (Type)null);
return null;
}
}
}