using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Text.Encodings.Web;
[JsonConverter(typeof(DoubleSerializedConverter))]
public T data {get; init;}
public class DoubleSerializedConverter : JsonConverterFactory
public override bool CanConvert(Type typeToConvert) => true;
class ObjectOrArrayKindConverter<T> : JsonConverter<T>
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
JsonTokenType.String => JsonSerializer.Deserialize<T>(reader.GetString()!, options),
_ => JsonSerializer.Deserialize<T>(ref reader, options),
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value, options);
class UnknownKindConverter<T> : JsonConverter<T>
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
JsonTokenType.String => JsonSerializer.Deserialize<T>(reader.GetString()!, options),
_ => JsonSerializer.Deserialize<T>(ref reader, options),
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value, options);
public sealed override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
var typeInfo = JsonSerializerOptions.Default.GetTypeInfo(typeToConvert);
var genericConverterType = (typeInfo.Kind == JsonTypeInfoKind.None ? typeof(UnknownKindConverter<>) : typeof(ObjectOrArrayKindConverter<>));
return (JsonConverter)Activator.CreateInstance(genericConverterType.MakeGenericType(typeToConvert))!;
public class SomeComplexClass
public string Foo { get; set; } = "";
public class OtherMoreDifferentClass
public string Bar { get; set; } = "";
public SomeComplexClass? SomeComplexClass { get; set; }
public static void Test()
TestRoundTrip(new SomeComplexClass { Foo = "hello" });
TestRoundTrip(new OtherMoreDifferentClass { Bar = "hello", SomeComplexClass = new() { Foo = "hello" } });
TestRoundTrip(new [] { new OtherMoreDifferentClass { Bar = "hello", SomeComplexClass = new() { Foo = "hello" } }});
TestRoundTrip("\"hello\"");
TestNonDoubleSerializedRoundTrip(new SomeComplexClass { Foo = "hello" });
TestNonDoubleSerializedRoundTrip(new OtherMoreDifferentClass { Bar = "hello", SomeComplexClass = new() { Foo = "hello" } });
TestNonDoubleSerializedRoundTrip(new [] { new OtherMoreDifferentClass { Bar = "hello", SomeComplexClass = new() { Foo = "hello" } }});
public static void TestRoundTrip<T>(T data)
var opts = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
var dataJson = JsonSerializer.Serialize(data, opts);
var json = JsonSerializer.Serialize(new Foo<string> { data = dataJson }, opts );
Console.WriteLine("Input JSON: {0}", json);
var foo = JsonSerializer.Deserialize<Foo<T>>(json);
Assert.AreEqual(dataJson, JsonSerializer.Serialize(foo!.data, opts));
public static void TestNonDoubleSerializedRoundTrip<T>(T data)
var json = JsonSerializer.Serialize(new Foo<T> { data = data }, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping } );
Console.WriteLine("Input JSON: {0}", json);
var foo = JsonSerializer.Deserialize<Foo<T>>(json);
Assert.AreEqual(JsonSerializer.Serialize(data), JsonSerializer.Serialize(foo!.data));
public static void Main()
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");