Skip to content

Instantly share code, notes, and snippets.

@AndreSteenveld
Created February 2, 2022 04:47
Show Gist options
  • Save AndreSteenveld/70328da36039eb349327436526aaa82e to your computer and use it in GitHub Desktop.
Save AndreSteenveld/70328da36039eb349327436526aaa82e to your computer and use it in GitHub Desktop.
System.Text.Json tuple (de)serializer
// This work is licensed under a Creative Commons Attribution 4.0 International License
// If you find a bug or some issue you can find me (and the orginal of this file) at https://gist.github.com/AndreSteenveld
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using Json = System.Text.Json;
using System.Text.Json.Serialization;
namespace AndreSteenveld
{
public class TupleAsArrayFactory : JsonConverterFactory
{
private static readonly Type?[] SystemTuple =
{
null,
typeof(Tuple<>), typeof(Tuple<,>), typeof(Tuple<,,>), typeof(Tuple<,,,>),
typeof(Tuple<,,,,>), typeof(Tuple<,,,,,>), typeof(Tuple<,,,,,,>), typeof(Tuple<,,,,,,,>)
};
private static readonly Type?[] ValueTuple =
{
null,
typeof(ValueTuple<>), typeof(ValueTuple<,>), typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>), typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
};
private static readonly Type?[] SystemTupleConverter =
{
null,
typeof(TupleAsArrayConverter<>.SystemTuple), typeof(TupleAsArrayConverter<,>.SystemTuple),
typeof(TupleAsArrayConverter<,,>.SystemTuple), typeof(TupleAsArrayConverter<,,,>.SystemTuple),
typeof(TupleAsArrayConverter<,,,,>.SystemTuple), typeof(TupleAsArrayConverter<,,,,,>.SystemTuple),
typeof(TupleAsArrayConverter<,,,,,,>.SystemTuple), typeof(TupleAsArrayConverter<,,,,,,>.SystemTuple<>)
};
private static readonly Type?[] ValueTupleConverter =
{
null,
typeof(TupleAsArrayConverter<>.ValueTuple), typeof(TupleAsArrayConverter<,>.ValueTuple),
typeof(TupleAsArrayConverter<,,>.ValueTuple), typeof(TupleAsArrayConverter<,,,>.ValueTuple),
typeof(TupleAsArrayConverter<,,,,>.ValueTuple), typeof(TupleAsArrayConverter<,,,,,>.ValueTuple),
typeof(TupleAsArrayConverter<,,,,,,>.ValueTuple), typeof(TupleAsArrayConverter<,,,,,,>.ValueTuple<>)
};
private static readonly Type[] TupleTypes = Array
.Empty<Type>()
.Append(ValueTuple[1..])
.Append(SystemTuple[1..])
.Cast<Type>()
.ToArray();
public override bool CanConvert(Type type) => type.IsGenericType && TupleTypes.Contains(type.GetGenericTypeDefinition());
public override JsonConverter? CreateConverter(Type type, Json.JsonSerializerOptions options)
{
var arguments = type.GetGenericArguments();
var generic = type.GetGenericTypeDefinition();
var converter = SystemTuple[arguments.Length] == generic ? SystemTupleConverter[arguments.Length]!.MakeGenericType(arguments)
: ValueTuple[arguments.Length] == generic ? ValueTupleConverter[arguments.Length]!.MakeGenericType(arguments)
: throw new NotSupportedException();
return (JsonConverter) Activator.CreateInstance(converter);
}
}
public class TupleAsArrayConverter<T1>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1>>
{
public override Tuple<T1>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1>>
{
public override ValueTuple<T1> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
}
public class TupleAsArrayConverter<T1, T2>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple)Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1, T2>>
{
public override Tuple<T1, T2>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1, T2>>
{
public override ValueTuple<T1, T2> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
}
public class TupleAsArrayConverter<T1, T2, T3>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T3), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
(options.GetConverter(typeof(T3)) as JsonConverter<T3>)!.Write(writer, (T3)tuple[2], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1, T2, T3>>
{
public override Tuple<T1, T2,T3>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2, T3>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2, T3> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1, T2, T3>>
{
public override ValueTuple<T1, T2, T3> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2, T3>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2, T3> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
}
public class TupleAsArrayConverter<T1, T2, T3, T4>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T3), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T4), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
(options.GetConverter(typeof(T3)) as JsonConverter<T3>)!.Write(writer, (T3)tuple[2], options);
(options.GetConverter(typeof(T4)) as JsonConverter<T4>)!.Write(writer, (T4)tuple[3], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1, T2, T3, T4>>
{
public override Tuple<T1, T2, T3, T4>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2, T3, T4>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2, T3, T4> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1, T2, T3, T4>>
{
public override ValueTuple<T1, T2, T3, T4> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2, T3, T4>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2, T3, T4> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
}
public class TupleAsArrayConverter<T1, T2, T3, T4, T5>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T3), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T4), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T5), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
(options.GetConverter(typeof(T3)) as JsonConverter<T3>)!.Write(writer, (T3)tuple[2], options);
(options.GetConverter(typeof(T4)) as JsonConverter<T4>)!.Write(writer, (T4)tuple[3], options);
(options.GetConverter(typeof(T5)) as JsonConverter<T5>)!.Write(writer, (T5)tuple[4], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1, T2, T3, T4, T5>>
{
public override Tuple<T1, T2, T3, T4, T5>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2, T3, T4, T5>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2, T3, T4, T5> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1, T2, T3, T4, T5>>
{
public override ValueTuple<T1, T2, T3, T4, T5> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2, T3, T4, T5>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2, T3, T4, T5> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
}
public class TupleAsArrayConverter<T1, T2, T3, T4, T5, T6>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T3), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T4), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T5), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T6), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
(options.GetConverter(typeof(T3)) as JsonConverter<T3>)!.Write(writer, (T3)tuple[2], options);
(options.GetConverter(typeof(T4)) as JsonConverter<T4>)!.Write(writer, (T4)tuple[3], options);
(options.GetConverter(typeof(T5)) as JsonConverter<T5>)!.Write(writer, (T5)tuple[4], options);
(options.GetConverter(typeof(T6)) as JsonConverter<T6>)!.Write(writer, (T6)tuple[5], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1, T2, T3, T4, T5, T6>>
{
public override Tuple<T1, T2, T3, T4, T5, T6>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2, T3, T4, T5, T6>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2, T3, T4, T5, T6> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1, T2, T3, T4, T5, T6>>
{
public override ValueTuple<T1, T2, T3, T4, T5, T6> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2, T3, T4, T5, T6>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2, T3, T4, T5, T6> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
}
public class TupleAsArrayConverter<T1, T2, T3, T4, T5, T6, T7>
{
private static ITuple? read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T3), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T4), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T5), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T6), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T7), options) : throw new JsonException()
};
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
}
private static void write(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
(options.GetConverter(typeof(T3)) as JsonConverter<T3>)!.Write(writer, (T3)tuple[2], options);
(options.GetConverter(typeof(T4)) as JsonConverter<T4>)!.Write(writer, (T4)tuple[3], options);
(options.GetConverter(typeof(T5)) as JsonConverter<T5>)!.Write(writer, (T5)tuple[4], options);
(options.GetConverter(typeof(T6)) as JsonConverter<T6>)!.Write(writer, (T6)tuple[5], options);
(options.GetConverter(typeof(T7)) as JsonConverter<T7>)!.Write(writer, (T7)tuple[6], options);
writer.WriteEndArray();
}
public class SystemTuple : JsonConverter<Tuple<T1, T2, T3, T4, T5, T6, T7>>
{
public override Tuple<T1, T2, T3, T4, T5, T6, T7>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2, T3, T4, T5, T6, T7>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2, T3, T4, T5, T6, T7> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
public class ValueTuple : JsonConverter<ValueTuple<T1, T2, T3, T4, T5, T6, T7>>
{
public override ValueTuple<T1, T2, T3, T4, T5, T6, T7> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2, T3, T4, T5, T6, T7>) read(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2, T3, T4, T5, T6, T7> tuple, Json.JsonSerializerOptions options) => write(writer, tuple, options);
}
//
//
//
private static ITuple? read<TRest>(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var values = new[]
{
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T1), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T2), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T3), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T4), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T5), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T6), options) : throw new JsonException(),
reader.Read() ? JsonSerializer.Deserialize(ref reader, typeof(T7), options) : throw new JsonException(),
reader.Read() ? DeserializeRest(ref reader, typeof(TRest), options) : throw new JsonException()
};
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException();
return (ITuple) Activator.CreateInstance(type, values);
object? DeserializeRest(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options)
{
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
writer.WriteStartArray();
while (reader.TokenType != JsonTokenType.EndArray)
{
switch (reader.TokenType)
{
case JsonTokenType.True:
case JsonTokenType.False:
case JsonTokenType.Number:
case JsonTokenType.Null:
writer.WriteRawValue(reader.ValueSpan, true);
break;
case JsonTokenType.String:
writer.WriteStringValue(reader.GetString());
break;
case JsonTokenType.StartObject:
case JsonTokenType.StartArray:
writer.WriteRawValue(reader.ValueSequence.ToArray(), true);
break;
default: throw new JsonException();
}
reader.Read();
}
writer.WriteEndArray();
writer.Flush();
return JsonSerializer.Deserialize(Encoding.UTF8.GetString(stream.ToArray()), type, options);
}
}
private static void write<TRest>(Utf8JsonWriter writer, ITuple tuple, Json.JsonSerializerOptions options)
{
writer.WriteStartArray();
(options.GetConverter(typeof(T1)) as JsonConverter<T1>)!.Write(writer, (T1)tuple[0], options);
(options.GetConverter(typeof(T2)) as JsonConverter<T2>)!.Write(writer, (T2)tuple[1], options);
(options.GetConverter(typeof(T3)) as JsonConverter<T3>)!.Write(writer, (T3)tuple[2], options);
(options.GetConverter(typeof(T4)) as JsonConverter<T4>)!.Write(writer, (T4)tuple[3], options);
(options.GetConverter(typeof(T5)) as JsonConverter<T5>)!.Write(writer, (T5)tuple[4], options);
(options.GetConverter(typeof(T6)) as JsonConverter<T6>)!.Write(writer, (T6)tuple[5], options);
(options.GetConverter(typeof(T7)) as JsonConverter<T7>)!.Write(writer, (T7)tuple[6], options);
for (int i = 7; i < tuple.Length; i++)
{
var type = tuple[i].GetType();
dynamic value = Convert.ChangeType(tuple[i], type);
dynamic converter = options.GetConverter(type);
converter.Write(writer, value, options);
}
writer.WriteEndArray();
}
public class SystemTuple<TRest> : JsonConverter<Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>>
{
public override Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>? Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>) read<TRest>(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> tuple, Json.JsonSerializerOptions options) => write<TRest>(writer, tuple, options);
}
public class ValueTuple<TRest> : JsonConverter<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>> where TRest : struct
{
public override ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> Read(ref Utf8JsonReader reader, Type type, Json.JsonSerializerOptions options) => (ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>) read<TRest>(ref reader, type, options)!;
public override void Write(Utf8JsonWriter writer, ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> tuple, Json.JsonSerializerOptions options) => write<TRest>(writer, tuple, options);
}
}
}
// This work is licensed under a Creative Commons Attribution 4.0 International License
// If you find a bug or some issue you can find me (and the orginal of this file) at https://gist.github.com/AndreSteenveld
using System;
using System.Text.Json;
using Json = System.Text.Json;
using Xunit;
using Tuple = System.Tuple;
namespace AndreSteenveld.Test
{
public class SanityChecksOnTupleConversionsUsingSystemTextJson
{
[Fact]
public void default_behaviour_for_system_text_json_serialize()
{
//
// We want to check the default behaviour of (de)Serializing System.Tuple<>, as this is the class
// we can use to create adhoc database types in our results.
//
var tuple = Tuple.Create(1, true, "sure");
//var desired = @"[1, true, ""sure""]";
// We would like a mixed array type as it is really convenient to express tuples with it and vice versa.
var expected = @"{""Item1"":1,""Item2"":true,""Item3"":""sure""}";
var result = JsonSerializer.Serialize(tuple);
Assert.Equal(expected, result);
}
[Fact]
public void default_behaviour_for_system_text_json_deserialize()
{
var json = @"[1, true, ""sure"", null]";
var expected = Tuple.Create<int, bool, string, int?>(1, true, "sure", null);
var result = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize(json, expected.GetType()));
// Well we got our exception here... So we can safely conclude to deserialize Tuple types we will have to
// write our own converter
//Assert.Equal(expected, result);
}
[Fact]
public void default_behaviour_for_value_tuple_deserialize()
{
var json = @"[1, true, ""sure"", null]";
(int, bool, string, int?) expected = (1, true, "sure", null);
var result = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize(json, expected.GetType()));
// Same for value tuples... no cigar.
//Assert.Equal(expected, result);
}
[Fact]
public void deserialize_array_to_system_tuple()
{
var json = "[1,2]";
var tuple = Tuple.Create(1, 2);
var options = new Json.JsonSerializerOptions();
options.Converters.Add(new TupleAsArrayFactory());
var result = JsonSerializer.Deserialize(json, tuple.GetType(), options);
Assert.Equal(tuple, result);
}
[Fact]
public void serialize_system_tuple_to_array()
{
var json = "[1,2]";
var tuple = Tuple.Create(1, 2);
var options = new Json.JsonSerializerOptions();
options.Converters.Add(new TupleAsArrayFactory());
var result = JsonSerializer.Serialize(tuple, tuple.GetType(), options);
Assert.Equal(json, result);
}
[Fact]
public void deserialize_array_to_value_tuple()
{
var json = "[1,2]";
var tuple = (1, 2);
var options = new Json.JsonSerializerOptions();
options.Converters.Add(new TupleAsArrayFactory());
var result = JsonSerializer.Deserialize(json, tuple.GetType(), options);
Assert.Equal(tuple, result);
}
[Fact]
public void serialize_value_tuple_to_array()
{
var json = "[1,2]";
var tuple = (1, 2);
var options = new Json.JsonSerializerOptions();
options.Converters.Add(new TupleAsArrayFactory());
var result = JsonSerializer.Serialize(tuple, tuple.GetType(), options);
Assert.Equal(json, result);
}
}
public class TupleConverter
{
public static object[][] SystemTuple => new[]
{
new object[] {@"[1,2]", Tuple.Create<int, int>(1, 2)},
new object[] {@"[""yes"",""no""]", Tuple.Create<string, string>("yes", "no")},
new object[] {@"[null,null]", Tuple.Create<int?, int?>(null, null)},
new object[] {@"[[1,2],[3,4]]", Tuple.Create(Tuple.Create(1,2), Tuple.Create(3,4))},
new object[]
{
@"[""one""]",
Tuple.Create("one")
},
new object[]
{
@"[""one"",""two""]",
Tuple.Create("one", "two")
},
new object[]
{
@"[""one"",""two"",""three""]",
Tuple.Create("one", "two", "three")
},
new object[]
{
@"[""one"",""two"",""three"",""four""]",
Tuple.Create("one", "two", "three", "four")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five""]",
Tuple.Create("one", "two", "three", "four", "five")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six""]",
Tuple.Create("one", "two", "three", "four", "five", "six")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six"",""seven""]",
Tuple.Create("one", "two", "three", "four", "five", "six", "seven")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six"",""seven"",""eight""]",
Tuple.Create("one", "two", "three", "four", "five", "six", "seven", "eight")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six"",""seven"",""eight"",""nine""]",
new Tuple
<
string, string, string, string, string, string, string,
Tuple<string, string>
>
(
"one", "two", "three", "four", "five", "six", "seven",
Tuple.Create("eight", "nine")
)
},
};
public static object[][] ValueTuple => new []
{
new object[] {@"[1,2]", (1, 2)},
new object[] {@"[""yes"",""no""]", ("yes", "no")},
new object[] {@"[null,null]", ((int?, int?))(null, null)},
new object[] {@"[[1,2],[3,4]]", ((1, 2), (3, 4))},
new object[]
{
@"[""one""]",
new ValueTuple<string>("one")
},
new object[]
{
@"[""one"",""two""]",
("one", "two")
},
new object[]
{
@"[""one"",""two"",""three""]",
("one", "two", "three")
},
new object[]
{
@"[""one"",""two"",""three"",""four""]",
("one", "two", "three", "four")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five""]",
("one", "two", "three", "four", "five")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six""]",
("one", "two", "three", "four", "five", "six")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six"",""seven""]",
("one", "two", "three", "four", "five", "six", "seven")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six"",""seven"",""eight""]",
("one", "two", "three", "four", "five", "six", "seven", "eight")
},
new object[]
{
@"[""one"",""two"",""three"",""four"",""five"",""six"",""seven"",""eight"",""nine""]",
("one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
}
};
[Theory, MemberData(nameof(ValueTuple))]
public void serialize_value_tuple(string json, object tuple) => Serialize(json, tuple);
[Theory, MemberData(nameof(SystemTuple))]
public void serialize_system_tuple(string json, object tuple) => Serialize(json, tuple);
[Theory, MemberData(nameof(ValueTuple))]
public void deserialize_value_tuple(string json, object tuple) => Deserialize(json, tuple);
[Theory, MemberData(nameof(SystemTuple))]
public void deserialize_system_tuple(string json, object tuple) => Deserialize(json, tuple);
private void Serialize(string json, object tuple)
{
var options = new Json.JsonSerializerOptions();
options.Converters.Add(new TupleAsArrayFactory());
var result = JsonSerializer.Serialize(tuple, tuple.GetType(), options);
Assert.Equal(json, result);
}
private void Deserialize(string json, object tuple)
{
var options = new Json.JsonSerializerOptions();
options.Converters.Add(new TupleAsArrayFactory());
var result = JsonSerializer.Deserialize(json, tuple.GetType(), options);
Assert.Equal(tuple, result);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment