Skip to content

Instantly share code, notes, and snippets.

@ncruces
Last active May 9, 2017 01:40
Show Gist options
  • Save ncruces/2f83a2e1430d5b05132dd43ffc7b59ba to your computer and use it in GitHub Desktop.
Save ncruces/2f83a2e1430d5b05132dd43ffc7b59ba to your computer and use it in GitHub Desktop.
Converts a Tuple to and from a JSON array
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Reflection;
namespace Newtonsoft.Json.Converters
{
/// <summary>
/// Converts a <see cref="Tuple"/> to and from a JSON array.
/// </summary>
public class TupleConverter : JsonConverter
{
public override bool CanConvert(Type type)
{
while (type != null)
{
var info = type.GetTypeInfo();
if (info.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(Tuple<>) ||
genType == typeof(Tuple<,>) ||
genType == typeof(Tuple<,,>) ||
genType == typeof(Tuple<,,,>) ||
genType == typeof(Tuple<,,,,>) ||
genType == typeof(Tuple<,,,,,>) ||
genType == typeof(Tuple<,,,,,,>) ||
genType == typeof(Tuple<,,,,,,,>) ||
genType == typeof(Tuple<,,,,,,,>))
return true;
}
type = info.BaseType;
}
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
if (reader.TokenType == JsonToken.StartArray)
{
reader.Read();
var tuple = ReadTuple(reader, objectType, serializer);
if (reader.TokenType == JsonToken.EndArray) return tuple;
}
throw new JsonSerializationException("Unexpected token parsing tuple.");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, EnumerateTuple(value));
}
static object ReadTuple(JsonReader reader, Type type, JsonSerializer serializer)
{
var types = type.GenericTypeArguments;
var args = new object[types.Length];
for (var i = 0; i < types.Length; ++i)
{
if (reader.TokenType == JsonToken.EndArray) throw new JsonSerializationException("Unexpected token parsing tuple.");
if (i < 7)
{
args[i] = serializer.Deserialize(reader, types[i]);
reader.Read();
}
else
{
args[i] = ReadTuple(reader, types[i], serializer);
}
}
return Activator.CreateInstance(type, args);
}
static IEnumerable EnumerateTuple(object value)
{
tailcall:
var type = value.GetType().GetTypeInfo();
var types = type.GenericTypeArguments;
for (var i = 1; i <= types.Length; ++i)
{
if (i < 8)
{
var prop = type.GetDeclaredProperty("Item" + i);
if (prop == null) throw new JsonSerializationException("Unexpected value when converting tuple.");
yield return prop.GetValue(value);
}
else
{
var prop = type.GetDeclaredProperty("Rest");
if (prop == null) throw new JsonSerializationException("Unexpected value when converting tuple.");
value = prop.GetValue(value);
goto tailcall;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment