I am attempting to use Newtonsoft.json DefaultContractResolver to output json. When the ContractResolver finds a class of type IVariant, it sets the output based upon the IVariant's "TypeName"
Without my ContractResolver code the json would look something like this:
{
  "Data" :
  {
    "Aim" :
      {
        "Joystick" :
        {
          "Test" : "Test"
        }, 
        "Button" :
        {
          "Test" : "Test2"
        }
      }
  }
}
and with my resolver, It replaces "Aim" with either Joystick, or Button, and look like this:
{
  "Data" :
  {
    "Button" :
    {
      "Test" : "Test2"
    }
  }
}
Below is the full code I used.
  public class JsonTester
  {
    public void Test()
    {
      var d = new Outer();
      var json = JsonConvert.SerializeObject(d, new JsonSerializerSettings()
      {
        ContractResolver = new JsonContractResolver()
      });
    }
  }
  public interface IVariant
  {
    object Data { get; }
    string TypeName { get; set; }
  }
  public interface IVariantData
  {
  }
  [DataContract]
  public class Variant<TEnum, TData> : IVariant where TEnum : struct where TData : class, IVariantData, new()
  {
    [DataMember]
    public TData Data { get; }
    public string TypeName { get; set; }
    object IVariant.Data => Data;
    public Variant()
    {
      Data = new TData();
    }
  }
  [DataContract]
  public sealed class VariantHolder
  {
    [DataContract]
    public class Joystick
    {
      [DataMember]
      public string Test { get; set; } = "Test";
    }
    [DataContract]
    public class Button
  {
    [DataMember]
    public string Test2 { get; set; } = "Test2";
  }
    [DataContract]
    public sealed class AimΞVariant : Variant<AimΞVariant.VariantEnum, AimΞVariant.VariantData>
  {
    public AimΞVariant()
    {
      TypeName = "Button";
    }
    public enum VariantEnum : byte
    {
      Joystick,
      Button
    }
    [DataContract]
    public sealed class VariantData : IVariantData
    {
      [DataMember]
      Joystick _Joystick = new Joystick();
      [DataMember]
      Button _Button = new Button();
      public Button Button => _Button;
      public Joystick Joystick => _Joystick;
    }
  }
    [DataMember]
    AimΞVariant Aim = new AimΞVariant();
  }
  [DataContract]
  public sealed class Outer
  {
    [DataMember]
    VariantHolder Data = new VariantHolder();
  }
  public class JsonContractResolver : DefaultContractResolver
  {
    public JsonContractResolver()
    {
    }
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
      return base.CreateProperty(member, memberSerialization);
    }
    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 =>
    {
      var member = p.GetCustomAttribute<DataMemberAttribute>();
      return new JsonProperty()
      {
        PropertyName = p.Name,
        PropertyType = p.PropertyType,
        Readable = true,
        Writable = true,
        ValueProvider = CreateMemberValueProvider(p),
        DefaultValueHandling = DefaultValueHandling.Include
      };
    }).Concat(
    type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(x => x.GetCustomAttributes().Any(a => a.GetType() == typeof(DataMemberAttribute))).Select(p =>
    {
      var member = p.GetCustomAttribute<DataMemberAttribute>();
      return new JsonProperty()
      {
        PropertyName = p.Name,
        PropertyType = p.FieldType,
        Readable = true,
        Writable = true,
        ValueProvider = CreateMemberValueProvider(p),
        DefaultValueHandling = DefaultValueHandling.Include
      };
    })).ToList();
    return list;
  }
    protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
    {
      Type type = member.MemberType == MemberTypes.Property ? ((PropertyInfo)member).PropertyType : ((FieldInfo)member).FieldType;
      if (typeof(IVariant).IsAssignableFrom(type))
      {
        return new IVariantValueProvider(member, type);
      }
      return base.CreateMemberValueProvider(member);
    }
  }
  internal class IVariantValueProvider : IValueProvider
  {
    private MemberInfo member;
    private Type type;
    public IVariantValueProvider(MemberInfo member, Type type)
    {
      this.member = member;
      this.type = type;
    }
    public object GetValue(object target)
    {
      IVariant variant;
      
      switch (member)
      {
      case PropertyInfo p:
        variant = (IVariant)p.GetValue(target);
        break;
      case FieldInfo f:
        variant = (IVariant)f.GetValue(target);
        break;
      default:
        throw new InvalidCastException();
      }
      var dataType = variant.TypeName;
      var property = variant.Data.GetType().GetProperty(dataType);
      var value = property.GetValue(variant.Data);
      return value;
    }
    public void SetValue(object target, object value)
    {
      throw new NotImplementedException();
    }
  }
The code crashes somewhere inside JsonConvert.SeralizeObject.
Newtonsoft.Json.JsonSerializationException: 'Error getting value from 'Data' on 'Iugo.Test.VariantHolder+Button'.' Inner Exception: InvalidCastException: Unable to cast object of type 'Button' to type 'Iugo.Test.Variant`2[Iugo.Test.VariantHolder+AimΞVariant+VariantEnum,Iugo.Test.VariantHolder+AimΞVariant+VariantData]'.
 
    