using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public class UserInfo : ValueOf<(string name, (int age, string eyes, DOB dob) features), UserInfo> { }
public class DOB : ValueOf<(int day, int month, int year), DOB> { }
public static void Main()
var ur = UserInfo.From(("harry", (36, "blue", DOB.From((22, 12, 81)))));
var ccr = new ValueTupleContractResolver();
JsonConvert.SerializeObject(ur).Dump();
var json = JsonConvert.SerializeObject(ur, new JsonSerializerSettings { ContractResolver = ccr, Converters = { new ValueOfConverter()} });
var deserialized = JsonConvert.DeserializeObject<UserInfo>(json, new JsonSerializerSettings { ContractResolver = ccr, Converters = { new ValueOfConverter()} });
public class ValueTupleContractResolver : DefaultContractResolver
Stack<Queue<string>> names = new Stack<Queue<String>>();
protected override JsonContract CreateContract(Type objectType)
var jc = base.CreateContract(objectType);
var tena = objectType.GetCustomAttributes().OfType<TupleElementNamesAttribute>().SingleOrDefault();
names.Push(new Queue<string>(tena.TransformNames));
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
var property = base.CreateProperty(member, memberSerialization);
if (member.Name == "Value" && member.DeclaringType.IsConstructedGenericType && member.DeclaringType.GetGenericTypeDefinition() == typeof(ValueOf<,>))
property.Writable = true;
property.PropertyName = name;
class ValueOfConverter : JsonConverter
public override bool CanConvert(Type objectType)
if (objectType.BaseType.IsGenericType)
if (objectType.BaseType.GetGenericTypeDefinition() == typeof(ValueOf<,>))
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var jt = Newtonsoft.Json.Linq.JToken.ReadFrom(reader);
var valueType = objectType.BaseType.GetGenericArguments()[0];
var value = jt.ToObject(valueType, serializer);
var from = objectType.GetMethod("From", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
return from.Invoke(null, new[] { value});
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var innerValue = ((dynamic) value).Value;
serializer.Serialize(writer, innerValue);