using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class KeyFilteringDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
readonly HashSet<TKey> toSkip;
public KeyFilteringDictionaryConverter(IEnumerable<TKey> toSkip) => this.toSkip = toSkip?.ToHashSet() ?? throw new ArgumentNullException(nameof(toSkip));
public override void WriteJson(JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializer serializer) => serializer.Serialize(writer, new KeyFilteringDictionarySurrogate<TKey, TValue>(value, toSkip));
public override bool CanRead => false;
public override IDictionary<TKey, TValue> ReadJson(JsonReader reader, Type objectType, IDictionary<TKey, TValue> existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
public class KeyFilteringDictionarySurrogate<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
readonly IDictionary<TKey, TValue> dictionary;
readonly HashSet<TKey> toSkip;
public KeyFilteringDictionarySurrogate(IDictionary<TKey, TValue> dictionary, IEnumerable<TKey> toSkip) : this(dictionary, toSkip ?.ToHashSet()) { }
public KeyFilteringDictionarySurrogate(IDictionary<TKey, TValue> dictionary, HashSet<TKey> toSkip)
this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary));
this.toSkip = toSkip ?? throw new ArgumentNullException(nameof(toSkip));
public bool ContainsKey(TKey key) => !toSkip.Contains(key) && dictionary.ContainsKey(key);
public bool TryGetValue(TKey key, out TValue value)
if (toSkip.Contains(key))
return dictionary.TryGetValue(key, out value);
public TValue this[TKey key] => toSkip.Contains(key) ? throw new KeyNotFoundException() : dictionary[key];
public IEnumerable<TKey> Keys => this.Select(p => p.Key);
public IEnumerable<TValue> Values => this.Select(p => p.Value);
public int Count => this.Count();
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dictionary.Where(p => !toSkip.Contains(p.Key)).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public class IgnorePrivatePropertiesContractResolver : DefaultContractResolver
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
var jsonProperties = base.CreateProperties(type, memberSerialization);
foreach (var dataProperty in jsonProperties.Where(p => p.PropertyName == "Data" && p.Converter == null))
var keyValuePairTypes = dataProperty.PropertyType.GetDictionaryKeyValueTypes().ToList();
if (keyValuePairTypes.Count == 1 && keyValuePairTypes[0][0] == typeof(string))
var ignoreProperties = jsonProperties.Where(p => p.AttributeProvider.GetAttributes(typeof(PrivateFieldAttribute), true).Any()).Select(p => p.PropertyName).ToHashSet();
if (ignoreProperties.Count > 0)
dataProperty.Converter = (JsonConverter)Activator.CreateInstance(typeof(KeyFilteringDictionaryConverter<,>).MakeGenericType(keyValuePairTypes[0]), new object [] { ignoreProperties });
public static class TypeExtensions
public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
=> (type ?? throw new ArgumentNullException()).IsInterface ? new[] { type }.Concat(type.GetInterfaces()) : type.GetInterfaces();
public static IEnumerable<Type []> GetDictionaryKeyValueTypes(this Type type)
=> type.GetInterfacesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)).Select(t => t.GetGenericArguments());
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false)]
public class PrivateFieldAttribute : System.Attribute { }
public Dictionary<string, string> Data;
public string Name { get { return Data["Name"]; } set { Data.Add("Name", value); } }
public string NotPrivate { get { return Data["NotPrivate"]; } set { Data.Add("NotPrivate", value); } }
public static void Test()
Data = new Dictionary<string, string>
{"SomeAdditionalKey", "Some additional value"},
NotPrivate = "public value",
var resolver = new IgnorePrivatePropertiesContractResolver();
var settings = new JsonSerializerSettings
ContractResolver = resolver,
var json = JsonConvert.SerializeObject(test, Formatting.Indented, settings);
Assert.IsTrue(!json.Contains("private value"));
Assert.IsTrue(json.Contains("Some additional value"));
Assert.IsTrue(json.Contains("public value"));
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];