using System.Collections;
using System.Collections.Generic;
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> UnignoreProperties(Type type, params string [] properties) =>
if (type.IsAssignableFrom(typeInfo.Type) && typeInfo.Kind == JsonTypeInfoKind.Object)
foreach (var property in typeInfo.Properties.Where(p => ShouldUnignore(p, properties)))
property.Get ??= CreatePropertyGetter(property);
property.Set ??= CreatePropertySetter(property);
if (property.Get != null)
property.ShouldSerialize = null;
public static Action<JsonTypeInfo> UnignorePropertiesForDeserialize(Type type, params string [] properties) =>
if (type.IsAssignableFrom(typeInfo.Type) && typeInfo.Kind == JsonTypeInfoKind.Object)
foreach (var property in typeInfo.Properties.Where(p => ShouldUnignore(p, properties)))
property.Set ??= CreatePropertySetter(property);
static bool ShouldUnignore(JsonPropertyInfo property, string [] properties) =>
property.ShouldSerialize != null && property.AttributeProvider?.IsDefined(typeof(JsonIgnoreAttribute), true) == true && properties.Contains(property.GetMemberName());
delegate TValue RefFunc<TObject, TValue>(ref TObject arg);
static Func<object, object?>? CreatePropertyGetter(JsonPropertyInfo property) =>
property.GetPropertyInfo() is {} info && info.ReflectedType != null && info.GetGetMethod() is {} getMethod
? CreateGetter(info.ReflectedType, getMethod) : null;
static Func<object, object?>? CreateGetter(Type type, MethodInfo? method)
var myMethod = typeof(JsonExtensions).GetMethod(nameof(JsonExtensions.CreateGetterGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
return (Func<object, object?>)(myMethod.MakeGenericMethod(new[] { type, method.ReturnType }).Invoke(null, new[] { method })!);
static Func<object, object?> CreateGetterGeneric<TObject, TValue>(MethodInfo method)
throw new ArgumentNullException();
if(typeof(TObject).IsValueType)
var func = (RefFunc<TObject, TValue>)Delegate.CreateDelegate(typeof(RefFunc<TObject, TValue>), null, method);
return (o) => {var tObj = (TObject)o; return func(ref tObj); };
var func = (Func<TObject, TValue>)Delegate.CreateDelegate(typeof(Func<TObject, TValue>), method);
return (o) => func((TObject)o);
static Action<object,object?>? CreatePropertySetter(JsonPropertyInfo property) =>
property.GetPropertyInfo() is {} info && info.ReflectedType != null && info.GetSetMethod() is {} setMethod
? CreateSetter(info.ReflectedType, setMethod) : null;
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);
static string? GetMemberName(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.Name;
public string IgnoreMe { get; set; } = "";
public int AlsoIgnoreMe { get; set; }
public class ReadOnlyTest
public string IgnoreMe { get; } = "ignored read only";
public static void Test()
static void TestRoundTrip()
var myClass = new MyClass { IgnoreMe = "I Should Appear", AlsoIgnoreMe = 101 };
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver
Modifiers = { JsonExtensions.UnignoreProperties(typeof(MyClass),
nameof(MyClass.IgnoreMe), nameof(MyClass.AlsoIgnoreMe)) },
var json = JsonSerializer.Serialize(myClass, options);
var myClass2 = JsonSerializer.Deserialize<MyClass>(json, options);
Assert.AreEqual("{\"IgnoreMe\":\"I Should Appear\",\"AlsoIgnoreMe\":101}", json);
Assert.AreEqual(myClass.IgnoreMe, myClass2!.IgnoreMe);
Assert.AreEqual(myClass.AlsoIgnoreMe, myClass2!.AlsoIgnoreMe);
var json2 = JsonSerializer.Serialize(myClass2, options);
Assert.AreEqual(json, json2);
static void TestDeserializeOnly()
{"IgnoreMe":"I Should Appear","AlsoIgnoreMe":101}
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver
Modifiers = { JsonExtensions.UnignorePropertiesForDeserialize(typeof(MyClass),
nameof(MyClass.IgnoreMe), nameof(MyClass.AlsoIgnoreMe)) },
var myClass2 = JsonSerializer.Deserialize<MyClass>(json, options);
Assert.AreEqual(myClass2!.IgnoreMe, "I Should Appear");
Assert.AreEqual(myClass2!.AlsoIgnoreMe, 101);
var json2 = JsonSerializer.Serialize(myClass2, options);
Assert.AreEqual("{}", json2);
static void TestReadOnly()
var test = new ReadOnlyTest();
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver
Modifiers = { JsonExtensions.UnignoreProperties(typeof(ReadOnlyTest), nameof(ReadOnlyTest.IgnoreMe)) },
var json = JsonSerializer.Serialize(test, options);
Assert.AreEqual("{\"IgnoreMe\":\"ignored read only\"}", json);
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: ");