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 Dictionary<string, object> Identifier { get; set; }
public double Value1 { get; set; }
public string Value2 { get; set; }
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class MyJsonExtensionDataAttribute : Attribute
public class MyContractResolver : DefaultContractResolver
protected override JsonObjectContract CreateObjectContract(Type objectType)
var contract = base.CreateObjectContract(objectType);
if (contract.ExtensionDataGetter == null && contract.ExtensionDataSetter == null)
var dictionaryProperty = contract.Properties
.Where(p => typeof(IDictionary<string, object>).IsAssignableFrom(p.PropertyType) && p.Readable && p.Writable)
.Where(p => p.AttributeProvider.GetAttributes(typeof(MyJsonExtensionDataAttribute), false).Any())
if (dictionaryProperty != null)
dictionaryProperty.Ignored = true;
contract.ExtensionDataGetter = o =>
((IDictionary<string, object>)dictionaryProperty.ValueProvider.GetValue(o)).Select(p => new KeyValuePair<object, object>(p.Key, p.Value));
contract.ExtensionDataSetter = (o, key, value) =>
var dictionary = (IDictionary<string, object>)dictionaryProperty.ValueProvider.GetValue(o);
dictionary = (IDictionary<string, object>)this.ResolveContract(dictionaryProperty.PropertyType).DefaultCreator();
dictionaryProperty.ValueProvider.SetValue(o, dictionary);
dictionary.Add(key, value);
contract.ExtensionDataValueType = typeof(object);
static IContractResolver resolver = new MyContractResolver();
public static void Test()
var results = new List<MyDto>
Identifier = new Dictionary<string, object> { { "Car", "Ford" } },
Identifier = new Dictionary<string, object> { { "Train", "Bombardier" } },
var settings = new JsonSerializerSettings
ContractResolver = resolver,
var json = JsonConvert.SerializeObject(results, Formatting.Indented, settings);
var results2 = JsonConvert.DeserializeAnonymousType(json, results, settings);
var json2 = JsonConvert.SerializeObject(results2, Formatting.Indented, settings);
Console.WriteLine("Round-tripped {0}:", results2);
Console.WriteLine(json2);
Assert.AreEqual(json, json2);
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(GetExpectedJson())));
static string GetExpectedJson()
""Train"": ""Bombardier"",
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];