using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
[JsonConverter(typeof(VerbatimDictionaryConverter<string, string>))]
public IDictionary<string, string> CustomAttributes { get; set; }
public class VerbatimDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
[JsonDictionary(NamingStrategyType = typeof(DefaultNamingStrategy))]
class VerbatimDictionarySerializationSurrogate : IReadOnlyDictionary<TKey, TValue>
readonly IDictionary<TKey, TValue> dictionary;
public VerbatimDictionarySerializationSurrogate(IDictionary<TKey, TValue> dictionary)
throw new ArgumentNullException(nameof(dictionary));
this.dictionary = dictionary;
public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); }
public bool TryGetValue(TKey key, out TValue value) { return dictionary.TryGetValue(key, out value); }
public TValue this[TKey key] { get { return dictionary[key]; } }
public IEnumerable<TKey> Keys { get { return dictionary.Keys; } }
public IEnumerable<TValue> Values { get { return dictionary.Values; } }
public int Count { get { return dictionary.Count; } }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return dictionary.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public override void WriteJson(JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializer serializer)
serializer.Serialize(writer, new VerbatimDictionarySerializationSurrogate(value));
public override bool CanRead { get { return false; } }
public override IDictionary<TKey, TValue> ReadJson(JsonReader reader, Type objectType, IDictionary<TKey, TValue> existingValue, bool hasExistingValue, JsonSerializer serializer) { throw new NotImplementedException(); }
public static void Test()
var root = new RootObject
CustomAttributes = new Dictionary<string, string>()
{"Custom Attribute 1", "1"},
{"CustomAttribute 2", "2"}
var settings = new JsonSerializerSettings
ContractResolver = new CamelCasePropertyNamesContractResolver(),
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
Assert.IsTrue(((JObject)JToken.Parse(json)["customAttributes"]).Properties().Select(p => p.Name).SequenceEqual(root.CustomAttributes.Keys));
Assert.IsTrue(JsonConvert.DeserializeObject<RootObject>(json, settings).CustomAttributes.Keys.SequenceEqual(root.CustomAttributes.Keys));
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("Json.NET version: " + 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];