using System.Collections.Generic;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
public static void Main()
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
ContractResolver = new CamelCasePropertyNamesContractResolver()
var json1 = "{\"discriminator\":\"typeA\",\"value\":{\"propA\":\"sample data A\"}}";
var json2 = "{\"discriminator\":\"array\",\"value\":[{\"propB\":\"sample data B\"}]}";
var json3 = "{\"discriminator\":\"text\",\"value\":\"sample text\"}";
Console.WriteLine("json 1: " + json1);
Console.WriteLine("json 2: " + json2);
Console.WriteLine("json 3: " + json3);
var discUnion1 = JsonConvert.DeserializeObject<DiscriminatedUnion>(json1);
var discUnion2 = JsonConvert.DeserializeObject<DiscriminatedUnion>(json2);
var discUnion3 = JsonConvert.DeserializeObject<DiscriminatedUnion>(json3);
Console.WriteLine("union 1 value type: " + discUnion1.Value.GetType());
Console.WriteLine("union 1 propA: " + ((TypeA)discUnion1.Value).PropA);
Console.WriteLine("union 2 value type: " + discUnion2.Value.GetType());
Console.WriteLine("union 2 value[0].PropB: " + ((List<TypeB>)discUnion2.Value)[0].PropB);
Console.WriteLine("union 3 value type: " + discUnion3.Value.GetType());
Console.WriteLine("union 3 value: " + discUnion3.Value);
Console.WriteLine("Reserialized json 1: " + JsonConvert.SerializeObject(discUnion1));
Console.WriteLine("Reserialized json 2: " + JsonConvert.SerializeObject(discUnion2));
Console.WriteLine("Reserialized json 3: " + JsonConvert.SerializeObject(discUnion3));
[JsonConverter(typeof (CamelCaseStringEnumConverter))]
public enum DiscriminatorType
public static class DiscriminatorTypeExtensions
public static bool IsArrayType(this DiscriminatorType value)
return value == DiscriminatorType.Array;
[JsonConverter(typeof (UnionConverter))]
public class DiscriminatedUnion
public DiscriminatorType Discriminator
public class UnionTypeAttribute : Attribute {
public UnionTypeAttribute(DiscriminatorType discriminatorType) {
Type = discriminatorType;
public DiscriminatorType Type { get; set; }
[UnionType(DiscriminatorType.TypeA)]
[UnionType(DiscriminatorType.Array)]
public class CamelCaseStringEnumConverter : StringEnumConverter
public CamelCaseStringEnumConverter(): base ()
public class UnionConverter : JsonConverter
private static Dictionary<DiscriminatorType, Type> map = new Dictionary<DiscriminatorType, Type>();
public UnionConverter() : base() {
Assembly.GetExecutingAssembly().GetTypes().ToList().ForEach(t => {
var attr = t.GetCustomAttribute<UnionTypeAttribute>();
map[disc] = disc.IsArrayType() ? typeof(List<>).MakeGenericType(t) : t;
public override bool CanConvert(Type objectType)
return objectType == typeof (DiscriminatedUnion);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotSupportedException("can't write");
public override bool CanWrite
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var discUnion = new DiscriminatedUnion();
serializer.Populate(reader, discUnion);
if (map.TryGetValue(discUnion.Discriminator, out type))
discUnion.Value = ((JToken) discUnion.Value).ToObject(type);