using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Nodes;
public static partial class JsonExtensions
public static Action<JsonTypeInfo> SetupUnknownDerivedTypeHandling(bool objectsOnly = false) =>
if (typeInfo.Kind == JsonTypeInfoKind.None || typeInfo.Type.IsSealed || typeInfo.Type == typeof(object))
if (objectsOnly && typeInfo.Kind != JsonTypeInfoKind.Object)
if (typeInfo.PolymorphismOptions == null)
typeInfo.PolymorphismOptions = new() { DerivedTypes = { new(typeInfo.Type) } };
typeInfo.PolymorphismOptions.UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization;
public T? Data { get; set; }
public class SubFoo : Foo;
public class SubSubFoo : SubFoo;
public class FooList : List<Foo>;
public class SpecialFooList : FooList;
public class FooDictionary : Dictionary<string, Foo>;
public class SpecialFooDictionary : FooDictionary;
public static void Test()
Assert.DoesNotThrow(() => Serialize<Foo, Foo>(false));
Assert.DoesNotThrow(() => Serialize<Foo, Foo>(true));
Assert.Throws(Is.InstanceOf(typeof(Exception)), () => Serialize<Foo, SubFoo>(false));
Assert.Throws(Is.InstanceOf(typeof(Exception)), () => Serialize<Foo, SubFoo>(true));
Assert.DoesNotThrow(() => Serialize<FooList, FooList>(false));
Assert.DoesNotThrow(() => Serialize<FooList, FooList>(true));
Assert.Throws(Is.InstanceOf(typeof(Exception)), () => Serialize<FooList, SpecialFooList>(false));
Assert.DoesNotThrow(() => Serialize<FooList, SpecialFooList>(true));
Assert.DoesNotThrow(() => Serialize<FooDictionary, FooDictionary>(false));
Assert.DoesNotThrow(() => Serialize<FooDictionary, FooDictionary>(true));
Assert.Throws(Is.InstanceOf(typeof(Exception)), () => Serialize<FooDictionary, SpecialFooDictionary>(false));
Assert.DoesNotThrow(() => Serialize<FooDictionary, SpecialFooDictionary>(true));
Assert.DoesNotThrow(() => Serialize<object, int>(false));
static void Serialize<TBase, TDerived>(bool objectsOnly) where TDerived : TBase, new()
var options = new JsonSerializerOptions()
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.WithAddedModifier(JsonExtensions.SetupUnknownDerivedTypeHandling(objectsOnly)),
var json = JsonSerializer.Serialize<TBase>(new TDerived(), options);
public static class ObjectExtensions
public static T ThrowOnNull<T>(this T? value) where T : class => value ?? throw new ArgumentNullException();
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: ");