using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Debug = System.Console;
var json = JsonConvert.SerializeObject(d,
new JsonSerializerSettings()
ContractResolver = new JsonContractResolver()
public interface IVariant
string TypeName { get; set; }
public interface IVariantData
public class Variant<TEnum, TData> : IVariant where TEnum : struct where TData : class, IVariantData, new()
public TData Data { get; }
public string TypeName { get; set; }
object IVariant.Data => Data;
public sealed class VariantHolder
public string Test { get; set; } = "Test";
public string Test2 { get; set; } = "Test2";
public sealed class AimΞVariant : Variant<AimΞVariant.VariantEnum, AimΞVariant.VariantData>
public enum VariantEnum : byte
public sealed class VariantData : IVariantData
Joystick _Joystick = new Joystick();
Button _Button = new Button();
public Button Button => _Button;
public Joystick Joystick => _Joystick;
AimΞVariant Aim = new AimΞVariant();
public sealed class Outer
VariantHolder Data = new VariantHolder();
public class JsonContractResolver : DefaultContractResolver
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
var property = base.CreateProperty(member, memberSerialization);
if (typeof(IVariant).IsAssignableFrom(property.PropertyType))
property.ValueProvider = new IVariantValueProvider(member, property.PropertyType);
property.DefaultValueHandling = DefaultValueHandling.Include;
property.PropertyType = typeof(object);
Debug.WriteLine("Used IVariantValueProvider for {0}", property);
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
var list = type.GetProperties().Where(x => x.GetCustomAttributes().Any(a => a.GetType() == typeof(DataMemberAttribute))).Select(p =>
return CreateProperty(p, memberSerialization);
type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(x => x.GetCustomAttributes().Any(a => a.GetType() == typeof(DataMemberAttribute))).Select(p =>
return CreateProperty(p, memberSerialization);
internal class IVariantValueProvider : IValueProvider
private MemberInfo member;
public IVariantValueProvider(MemberInfo member, Type type)
public object GetValue(object target)
Debug.WriteLine("GetValue() called for {0}.{1}", target, member.Name);
variant = (IVariant)p.GetValue(target);
variant = (IVariant)f.GetValue(target);
throw new InvalidCastException();
var dataType = variant.TypeName;
var property = variant.Data.GetType().GetProperty(dataType);
var value = property.GetValue(variant.Data);
Debug.WriteLine(" Returned {0}", value);
public void SetValue(object target, object value)
throw new NotImplementedException();
public static void Test()
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];