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;
public class DictionaryMergeConverter : JsonConverter
static readonly IContractResolver defaultResolver = JsonSerializer.CreateDefault().ContractResolver;
readonly IContractResolver resolver = defaultResolver;
public override bool CanConvert(Type objectType)
var keyValueTypes = objectType.GetDictionaryKeyValueType();
if (keyValueTypes == null)
var keyContract = resolver.ResolveContract(keyValueTypes[0]);
if (!(keyContract is JsonPrimitiveContract))
var contract = resolver.ResolveContract(keyValueTypes[1]);
return contract is JsonContainerContract;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
IDictionary dictionary = existingValue as IDictionary ?? (IDictionary)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
var keyValueTypes = objectType.GetDictionaryKeyValueType();
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
switch (reader.TokenType)
case JsonToken.PropertyName:
var name = (string)reader.Value;
reader.ReadToContentAndAssert();
var key = (keyValueTypes[0] == typeof(string) ? (object)name : Convert.ChangeType(name, keyValueTypes[0], serializer.Culture));
var value = dictionary.Contains(key) ? dictionary[key] : null;
value = serializer.Deserialize(reader, keyValueTypes[1]);
serializer.Populate(reader, value);
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); }
public static partial class JsonExtensions
public static JsonReader ReadToContentAndAssert(this JsonReader reader)
return reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
throw new ArgumentNullException();
throw new JsonReaderException("Unexpected end of JSON stream.");
public static class TypeExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
public static Type[] GetDictionaryKeyValueType(this Type type)
return type.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>)).Select(t => t.GetGenericArguments()).FirstOrDefault();
public static partial class JsonExtensions
public static void PopulateObjectWithConverter(string value, object target, JsonSerializerSettings settings)
if (target == null || value == null)
throw new ArgumentNullException();
var serializer = JsonSerializer.CreateDefault(settings);
var converter = serializer.Converters.Where(c => c.CanConvert(target.GetType()) && c.CanRead).FirstOrDefault() ?? serializer.ContractResolver.ResolveContract(target.GetType()).Converter;
using (var jsonReader = new JsonTextReader(new StringReader(value)))
serializer.Populate(jsonReader, target);
jsonReader.MoveToContentAndAssert();
var newtarget = converter.ReadJson(jsonReader, target.GetType(), target, serializer);
throw new JsonException(string.Format("Converter {0} allocated a new object rather than populating the existing object {1}.", converter, value));
[JsonObject(MemberSerialization.OptIn)]
[JsonProperty(PropertyName = "val_prop")]
public int Val { get; set; }
public int OtherVal { get; set; }
public static void Test()
var to_serialize = CreateDictionary(new Model { Val = 100, OtherVal = 99900 }, new Model { Val = 101, OtherVal = 99901 });
var values = to_serialize.Select(p => new { p.Key, p.Value }).ToList();
var jsonString = JsonConvert.SerializeObject(to_serialize, Formatting.Indented);
var settings = new JsonSerializerSettings
Converters = { new DictionaryMergeConverter() },
JsonExtensions.PopulateObjectWithConverter(jsonString, to_serialize, settings);
Console.WriteLine(jsonString);
Assert.IsTrue(values.Count == to_serialize.Count);
Assert.IsTrue(values.Select(v => to_serialize[v.Key] == v.Value).All(b => b));
var to_serialize = CreateDictionary(new Model { Val = 100, OtherVal = 99900 }, new Model { Val = 101, OtherVal = 99901 });
var to_populate = CreateDictionary(new Model { Val = -1, OtherVal = -1 }, new Model { Val = -1, OtherVal = -1 });
var to_serialize_values = to_serialize.Select(p => new { p.Key, p.Value.Val, p.Value.OtherVal }).ToList();
var to_populate_values = to_populate.Select(p => new { p.Key, p.Value.Val, p.Value.OtherVal }).ToList();
var jsonString = JsonConvert.SerializeObject(to_serialize, Formatting.Indented);
var settings = new JsonSerializerSettings
Converters = { new DictionaryMergeConverter() },
JsonExtensions.PopulateObjectWithConverter(jsonString, to_populate, settings);
Console.WriteLine(jsonString);
var newJson = JsonConvert.SerializeObject(to_populate, Formatting.Indented);
Console.WriteLine(newJson);
Assert.IsTrue(to_populate.Count == to_populate.Count);
Assert.IsTrue(to_serialize_values.Select(v => to_populate[v.Key].Val == v.Val).All(b => b));
Assert.IsTrue(to_populate_values.Select(v => to_populate[v.Key].OtherVal == v.OtherVal).All(b => b));
static Dictionary<int, Model> CreateDictionary(params Model[] models)
return CreateDictionary((IEnumerable<Model>)models);
static Dictionary<int, Model> CreateDictionary(IEnumerable<Model> models)
return models.Select((m, i) => new { Key = i, Value = m }).ToDictionary(p => p.Key, p => p.Value);
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");