Last active
May 9, 2017 01:40
-
-
Save ncruces/2f83a2e1430d5b05132dd43ffc7b59ba to your computer and use it in GitHub Desktop.
Converts a Tuple to and from a JSON array
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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