using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Nodes;
public string Name { get; set; }
public string Value { get; set; }
public class TestClass<T> : BaseClass {
public T Tag { get; set; }
public abstract class AbstractMidClass : BaseClass;
public class LeafClass : AbstractMidClass;
public static partial class JsonExtensions
public static Action<JsonTypeInfo> AddPolymorphismOptions(Type baseType, JsonPolymorphismOptions options) => (typeInfo) =>
throw new ArgumentException($"Cannot add JsonPolymorphismOptions to sealed base type {baseType.FullName}");
if (typeInfo.Type.IsAssignableTo(baseType) && !typeInfo.Type.IsSealed)
typeInfo.PolymorphismOptions = new()
IgnoreUnrecognizedTypeDiscriminators = options.IgnoreUnrecognizedTypeDiscriminators,
TypeDiscriminatorPropertyName = options.TypeDiscriminatorPropertyName,
UnknownDerivedTypeHandling = options.UnknownDerivedTypeHandling,
foreach (var derivedType in options.DerivedTypes.Where(t => !t.DerivedType.IsAbstract && t.DerivedType.IsAssignableTo(typeInfo.Type)))
typeInfo.PolymorphismOptions.DerivedTypes.Add(derivedType);
public static void Test()
TestRoundTrip<BaseClass, TestClass<int>>(new () { Name = "name", Value = "value", Tag = 121 });
TestRoundTrip<TestClass<int>, TestClass<int>>(new () { Name = "name", Value = "value", Tag = 121 });
TestRoundTrip<BaseClass, LeafClass>(new () { Name = "name", Value = "value" });
TestRoundTrip<BaseClass, LeafClass>(new () { Name = "name", Value = "value" });
TestRoundTrip<AbstractMidClass, LeafClass>(new () { Name = "name", Value = "value" });
TestRoundTrip<AbstractMidClass, LeafClass>(new () { Name = "name", Value = "value" });
TestRoundTrip<LeafClass, LeafClass>(new () { Name = "name", Value = "value" });
TestRoundTrip<LeafClass, LeafClass>(new () { Name = "name", Value = "value" });
TestRoundTripWithoutWithAddedModifier<BaseClass, TestClass<int>>(new () { Name = "name", Value = "value", Tag = 121 });
TestRoundTripWithoutWithAddedModifier<TestClass<int>, TestClass<int>>(new () { Name = "name", Value = "value", Tag = 121 });
static void TestQuestion()
var options = new JsonSerializerOptions()
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.WithAddedModifier(JsonExtensions.AddPolymorphismOptions(
DerivedTypes = { new(typeof(BaseClass), "1"),
new(typeof(TestClass<int>), "2") },
var i = JsonSerializer.Serialize<BaseClass>(new TestClass<int>() { Name = "123", Value = "456", Tag = 1 }, options);
var j = JsonSerializer.Deserialize<BaseClass>(i, options);
if (typeof(TestClass<int>) != j?.GetType())
throw new ApplicationException("root.GetType() != root2.GetType()");
public static void TestRoundTrip<TBase, TDerived>(TDerived root) where TDerived : BaseClass, TBase
var options = new JsonSerializerOptions()
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.WithAddedModifier(JsonExtensions.AddPolymorphismOptions(
DerivedTypes = { new(typeof(BaseClass), "1"),
new(typeof(TestClass<int>), "2"),
new(typeof(LeafClass), "3")
TestRoundTrip<TBase, TDerived>(root, options);
public static void TestRoundTripWithoutWithAddedModifier<TBase, TDerived>(TDerived root) where TDerived : BaseClass, TBase
var options = new JsonSerializerOptions()
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
JsonExtensions.AddPolymorphismOptions(
DerivedTypes = { new(typeof(BaseClass), "1"),
new(typeof(TestClass<int>), "2"), },
TestRoundTrip<TBase, TDerived>(root, options);
public static void TestRoundTrip<TBase, TDerived>(TDerived root, JsonSerializerOptions options) where TDerived : BaseClass, TBase
var json = JsonSerializer.Serialize<TBase>(root, options);
var root2 = JsonSerializer.Deserialize<TBase>(json, options);
var json2 = JsonSerializer.Serialize<TBase>(root2!, options);
Console.WriteLine(json2);
throw new ApplicationException("json != json2");
if (root.GetType() != root2?.GetType())
throw new ApplicationException("root.GetType() != root2.GetType()");
if (JsonSerializer.Serialize<object>(root) != JsonSerializer.Serialize<object>(root2))
throw new ApplicationException("JsonSerializer.Serialize<object>(root) != JsonSerializer.Serialize<object>(root2)");
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: ");