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;
using Debug = System.Console;
[JsonDerivedType(typeof(Model), "Model")]
public string ModelName { get; internal set; }
public DateTime DateTime { get; private set; } = DateTime.UtcNow;
[JsonDerivedType(typeof(Model), "Model")]
public class Model : ModelBase
public List<Item> Items { get; private set; } = new();
public static Item Create(Model parent) => new() { Parent = parent };
public static Item Create(Model parent, string itemName) => new() { Parent = parent, ItemName = itemName };
public Model Parent { get; private set; }
public string ItemName { get; private set; }
public static partial class JsonExtensions
public static void DeserializePrivateSetters(JsonTypeInfo typeInfo)
if (typeInfo.Kind != JsonTypeInfoKind.Object)
foreach (var property in typeInfo.Properties)
if (property.Get != null && property.Set == null
&& property.GetPropertyInfo() is {} info
&& info.GetSetMethod(true) is {} setMethod)
property.Set = CreateSetter(typeInfo.Type, setMethod);
static Action<object,object?>? CreateSetter(Type type, MethodInfo? method)
var myMethod = typeof(JsonExtensions).GetMethod(nameof(JsonExtensions.CreateSetterGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
return (Action<object,object?>)(myMethod.MakeGenericMethod(new [] { type, method.GetParameters().Single().ParameterType }).Invoke(null, new[] { method })!);
static Action<object,object?>? CreateSetterGeneric<TObject, TValue>(MethodInfo method)
throw new ArgumentNullException();
if (typeof(TObject).IsValueType)
return (o, v) => method.Invoke(o, new [] { v });
var func = (Action<TObject, TValue?>)Delegate.CreateDelegate(typeof(Action<TObject, TValue?>), method);
return (o, v) => func((TObject)o, (TValue?)v);
static PropertyInfo? GetPropertyInfo(this JsonPropertyInfo property) => (property.AttributeProvider as PropertyInfo);
public static void Test()
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.WithAddedModifier(JsonExtensions.DeserializePrivateSetters),
ReferenceHandler = ReferenceHandler.Preserve,
ModelName = "my model name",
model.Items.AddRange(new [] { Item.Create(model, "Item 1"), Item.Create(model, "Item 2") });
model.Items.Add(model.Items[0]);
var json = JsonSerializer.Serialize<ModelBase>(model, options);
var model2 = (Model)JsonSerializer.Deserialize<ModelBase>(json, options)!;
var json2 = JsonSerializer.Serialize<ModelBase>(model2, options);
Assert.AreEqual(model.ModelName, model2.ModelName);
Assert.AreEqual(model.Items.Count, model2.Items.Count);
CollectionAssert.AreEqual(model.Items.Select(i => i.ItemName), model2.Items.Select(i => i.ItemName));
Assert.AreEqual(model.Items.Distinct().Count(), 2);
Assert.AreEqual(model2.Items.Distinct().Count(), 2);
Assert.AreEqual(json, json2);
Assert.AreEqual(model.DateTime, model2.DateTime);
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: ");