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;
}
}
}