Created
February 2, 2022 04:47
-
-
Save AndreSteenveld/70328da36039eb349327436526aaa82e to your computer and use it in GitHub Desktop.
System.Text.Json tuple (de)serializer
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
// 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 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
// 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