using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public interface IMyCommand
public class MyCommand1 : IMyCommand
public class MyCommand2 : IMyCommand
[JsonConverter(typeof(MyCommandSerializationBinderSettingConverter))]
public Guid Id { get; set; }
public string CompanyId { get; set; }
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
public List<IMyCommand> Commands { get; set; }
public MyClass(string partitionKey)
CompanyId = partitionKey;
internal class MyCommandSerializationBinderSettingConverter : KnownTypesJsonConverter<object>
public MyCommandSerializationBinderSettingConverter() : base(new KeyValuePair<string, Type>[]
new(nameof(MyCommand1), typeof(MyCommand1)),
new(nameof(MyCommand2), typeof(MyCommand2)),
public class KnownTypesJsonConverter<TValue> : RecursiveConverterBase<TValue>
readonly ISerializationBinder binder;
public KnownTypesJsonConverter(IEnumerable<KeyValuePair<string, Type>> namesToTypes) => this.binder = new KnownTypesSerializationBinder(namesToTypes);
public KnownTypesJsonConverter(ISerializationBinder binder) => this.binder = binder;
protected override void WriteJsonWithDefault(JsonWriter writer, TValue value, JsonSerializer serializer)
var old = serializer.SerializationBinder;
serializer.SerializationBinder = binder;
base.WriteJsonWithDefault(writer, value, serializer);
serializer.SerializationBinder = old;
protected override TValue ReadJsonWithDefault(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var old = serializer.SerializationBinder;
serializer.SerializationBinder = binder;
return base.ReadJsonWithDefault(reader, objectType, existingValue, serializer);
serializer.SerializationBinder = old;
public abstract class RecursiveConverterBase<TValue> : JsonConverter
static readonly ThreadLocal<Stack<Type>> typeStack = new (() => new Stack<Type>());
bool IsSuspended => typeStack.IsValueCreated && typeStack.Value.TryPeek(out var type) && type == GetType();
public override bool CanRead => !IsSuspended;
public override bool CanWrite => !IsSuspended;
public override bool CanConvert(Type objectType) => !IsSuspended && typeof(TValue).IsAssignableFrom(objectType);
protected virtual void WriteJsonWithDefault(JsonWriter writer, TValue value, JsonSerializer serializer)
Console.WriteLine(value);
serializer.Serialize(writer, value, typeof(TValue));
public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
using (var pushValue = typeStack.Value.PushUsing(GetType()))
WriteJsonWithDefault(writer, (TValue)value, serializer);
protected virtual TValue ReadJsonWithDefault(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => (TValue)serializer.Deserialize(reader, objectType);
public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
using (var pushValue = typeStack.Value.PushUsing(GetType()))
return ReadJsonWithDefault(reader, objectType, existingValue, serializer);
public static class StackExtensions
public class PushValue<T> : IDisposable
public PushValue(T value, Stack<T> stack)
this.count = stack.Count;
while (stack.Count > count)
public static PushValue<T> PushUsing<T>(this Stack<T> stack, T value)
throw new ArgumentNullException();
return new PushValue<T>(value, stack);
public class KnownTypesSerializationBinder : ISerializationBinder
Dictionary<Type, string> typesToNames;
Dictionary<string, Type> namesToTypes;
public KnownTypesSerializationBinder(IEnumerable<KeyValuePair<string, Type>> namesToTypes) =>
(this.namesToTypes, this.typesToNames) = (namesToTypes.ToDictionary(t => t.Key, t => t.Value), namesToTypes.ToDictionary(t => t.Value, t => t.Key));
public Type BindToType(string assemblyName, string typeName) => namesToTypes[typeName];
public void BindToName(Type serializedType, out string assemblyName, out string typeName) => (assemblyName, typeName) = (null, typesToNames[serializedType]);
public static void Test()
string partitionKey = "Test";
var myclass = new MyClass(partitionKey)
Commands = new List<IMyCommand>
var settings = new JsonSerializerSettings
var json = JsonConvert.SerializeObject(myclass, settings);
var myclass2 = JsonConvert.DeserializeObject<MyClass>(json, settings);
var json2 = JsonConvert.SerializeObject(myclass2, settings);
Console.WriteLine(json2);
public static void Main()
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Namespace, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");