using System.Collections.Generic;
using System.Diagnostics;
using System.Collections;
using System.Runtime.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class DowncastingConverter<TBase, TDerived> : PolymorphicCreationConverter<TBase> where TDerived : TBase
protected override TBase Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver contractResolver, JObject obj)
Type createType = objectType;
if (createType.IsAssignableFrom(polymorphicType))
createType = polymorphicType;
if (createType.IsAssignableFrom(typeof(TDerived)))
createType = typeof(TDerived);
if (existingValue != null && createType.IsAssignableFrom(existingValue.GetType()))
return (TBase)existingValue;
var contract = contractResolver.ResolveContract(createType);
return (TBase)contract.DefaultCreator();
public abstract class PolymorphicCreationConverter<T> : JsonConverter
public override bool CanConvert(Type objectType)
return typeof(T).IsAssignableFrom(objectType);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotSupportedException("CustomCreationConverter should only be used while deserializing.");
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Null)
var obj = JObject.Load(reader);
Type polymorphicType = null;
var polymorphicTypeString = (string)obj["$type"];
if (polymorphicTypeString != null)
if (serializer.TypeNameHandling != TypeNameHandling.None)
string typeName, assemblyName;
ReflectionUtils.SplitFullyQualifiedTypeName(polymorphicTypeString, out typeName, out assemblyName);
polymorphicType = serializer.Binder.BindToType(assemblyName, typeName);
var value = Create(objectType, polymorphicType, existingValue, serializer.ContractResolver, obj);
throw new JsonSerializationException("No object created.");
using (var subReader = obj.CreateReader())
serializer.Populate(subReader, value);
protected abstract T Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver iContractResolver, JObject obj);
public override bool CanWrite { get { return false; } }
internal static class ReflectionUtils
public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName)
int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
if (assemblyDelimiterIndex != null)
typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.GetValueOrDefault()).Trim();
assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1).Trim();
typeName = fullyQualifiedTypeName;
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
char current = fullyQualifiedTypeName[i];
class RowToRoleRuleConverter : CustomCreationConverter<Row>
public override Row Create(Type objectType)
if (objectType.IsAssignableFrom(typeof(RowRule)))
return Activator.CreateInstance<RowRule>();
return (Row)Activator.CreateInstance(objectType);
public IList<Row> Rows { get; set; }
public string Status { get; set; }
public string SomeOtherProperty { get; set; }
public static void Test()
cabin.Rows = new List<Row>()
new SomeOtherRow { SomeOtherProperty = "some other property" },
JsonSerializerSettings settings = new JsonSerializerSettings()
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full,
string json = JsonConvert.SerializeObject(cabin, Formatting.Indented, settings);
Console.WriteLine("Serialized Cabin: ");
JsonSerializerSettings readSettings = new JsonSerializerSettings()
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full,
Converters = new[] { new DowncastingConverter<Row, RowRule>() },
Cabin obj = JsonConvert.DeserializeObject<Cabin>(json, readSettings);
var json2 = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Console.WriteLine("Re-serialized Cabin: ");
Console.WriteLine(json2);
if (!cabin.Rows.Select(r => r.GetType() == typeof(Row) ? typeof(RowRule) : r.GetType()).SequenceEqual(obj.Rows.Select(r => r.GetType())))
throw new InvalidOperationException("!cabin.Rows.Select(r => r.GetType() == typeof(Row) ? typeof(RowRule) : r.GetType()).SequenceEqual(obj.Rows.Select(r => r.GetType()))");
Console.WriteLine("Row types are as expected.");
public static void Main()