using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
public class SelectivePropertiesTypeInfoResolver : DefaultJsonTypeInfoResolver
readonly HashSet<string> membersToSerialize;
public SelectivePropertiesTypeInfoResolver(string fields) : this(fields?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? throw new ArgumentNullException("fields")) { }
public SelectivePropertiesTypeInfoResolver(IEnumerable<string> membersToSerialize) =>
this.membersToSerialize = membersToSerialize?.ToHashSet() ?? throw new ArgumentNullException(nameof(membersToSerialize));
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
JsonTypeInfo typeInfo = base.GetTypeInfo(type, options);
if (typeInfo.Kind == JsonTypeInfoKind.Object)
var lookup = type.GetLikelyJsonNameToPropertyNameLookup();
foreach (var property in typeInfo.Properties)
if (!lookup[property.Name].Any(n => membersToSerialize.Contains(n)))
property.ShouldSerialize = static (obj, value) => false;
public static partial class JsonSerializerExtensions
static string GetJsonName(MemberInfo memberInfo, JsonSerializerOptions? options = default)
if (memberInfo.GetCustomAttribute<JsonPropertyNameAttribute>() is var attr && attr?.Name != null)
var name = memberInfo.Name;
return options?.PropertyNamingPolicy?.ConvertName(name) ?? name;
public static ILookup<string, string> GetLikelyJsonNameToPropertyNameLookup(this Type type, JsonSerializerOptions? options = default)
IEnumerable<MemberInfo> properties = type.GetProperties();
IEnumerable<MemberInfo> fields = type.GetFields().Where(f => options?.IncludeFields == true || Attribute.IsDefined(f, typeof(JsonIncludeAttribute)));
var members = properties.Concat(fields).Where(m => !(m.GetCustomAttribute<JsonIgnoreAttribute>() is var attr && attr?.Condition == JsonIgnoreCondition.Always));
var names = members.Select(m => (UnderlyingName : m.Name, JsonName : GetJsonName(m, options)));
return names.ToLookup(m =>m.JsonName, m => m.UnderlyingName, options?.PropertyNameCaseInsensitive == true ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);
public string? BaseProperty { get; set; }
public class FieldClass : BaseClass
[JsonPropertyName("FirstName foo bar")]
public string? FirstName;
public string? IgnoreMe { get; set; }
public string? DontIgnoreMe { get; set; }
public string? SomeProperty { get; set; }
[JsonPropertyName("SomeProperty")]
public string? SurrogateForSomeProperty { get { return SomeProperty + "+"; } set { SomeProperty = value?.TrimEnd('+'); } }
static Func<object,object?,bool>? ShouldSerialize { get; set; }
public static void Test()
ShouldSerialize = static (obj, value) => false;
var options = new JsonSerializerOptions
TypeInfoResolver = new SelectivePropertiesTypeInfoResolver("FirstName,BaseProperty"),
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
var model = new FieldClass { FirstName = "hello" };
Console.WriteLine(JsonSerializer.Serialize(model, options));
Console.WriteLine(JsonSerializer.Serialize(typeof(FieldClass)
.GetLikelyJsonNameToPropertyNameLookup(options)
.Select(l => new { l.Key, Values = l.ToArray() })
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: ");
public static partial class JsonSerializerExtensions
public static DefaultJsonTypeInfoResolver SerializeSelectedFields(this DefaultJsonTypeInfoResolver resolver, JsonSerializerOptions? options, IEnumerable<string> fieldNames)
throw new ArgumentNullException(nameof(resolver));
throw new ArgumentNullException(nameof(fieldNames));
var fieldNameSet = fieldNames.ToHashSet(StringComparer.OrdinalIgnoreCase);
resolver.Modifiers.Add(typeInfo =>
foreach (var propertyInfo in typeInfo.Properties)
if (!fieldNameSet.Contains(propertyInfo.Name))
propertyInfo.ShouldSerialize = static (obj, value) => false;