using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class UntypedToTypedValueContractResolver : DefaultContractResolver
static UntypedToTypedValueContractResolver instance;
static UntypedToTypedValueContractResolver() { instance = new UntypedToTypedValueContractResolver(); }
public static UntypedToTypedValueContractResolver Instance { get { return instance; } }
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
var contract = base.CreateDictionaryContract(objectType);
if (contract.DictionaryValueType == typeof(object) && contract.ItemConverter == null)
contract.ItemConverter = new UntypedToTypedValueConverter();
class UntypedToTypedValueConverter : JsonConverter
public override bool CanConvert(Type objectType)
throw new NotImplementedException("This converter should only be applied directly via ItemConverterType, not added to JsonSerializer.Converters");
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Null)
var value = serializer.Deserialize(reader, objectType);
if (value is TypeWrapper)
return ((TypeWrapper)value).ObjectValue;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
if (serializer.TypeNameHandling == TypeNameHandling.None)
Console.WriteLine("ObjectItemConverter used when serializer.TypeNameHandling == TypeNameHandling.None");
serializer.Serialize(writer, value);
else if (value is string)
writer.WriteValue((string)value);
writer.WriteValue((bool)value);
var contract = serializer.ContractResolver.ResolveContract(value.GetType());
if (contract is JsonPrimitiveContract)
var wrapper = TypeWrapper.CreateWrapper(value);
serializer.Serialize(writer, wrapper, typeof(object));
serializer.Serialize(writer, value);
public abstract class TypeWrapper
protected TypeWrapper() { }
public abstract object ObjectValue { get; }
public static TypeWrapper CreateWrapper<T>(T value)
return new TypeWrapper<T>();
var type = value.GetType();
return new TypeWrapper<T>(value);
return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value);
public sealed class TypeWrapper<T> : TypeWrapper
public TypeWrapper() : base() { }
public TypeWrapper(T value)
public override object ObjectValue { get { return Value; } }
public T Value { get; set; }
public enum Foo { A, B, C }
public enum Bar { A, B, C }
public class Misc { public Foo Foo { get; set; } }
public static class ObjectExtensions
public static Type GetTypeSafe(this object obj)
public static void Test()
var dict = new Dictionary<string, object>();
dict.Add("c", new Misc());
dict.Add("DateTime", DateTime.Today);
dict.Add("string", "a string value");
dict.Add("int", int.MaxValue);
dict.Add("uint", uint.MaxValue);
dict.Add("long", long.MaxValue - 1);
dict.Add("ulong", (ulong)long.MaxValue);
dict.Add("short", short.MaxValue);
dict.Add("ushort", ushort.MaxValue);
var settings = new JsonSerializerSettings
TypeNameHandling = TypeNameHandling.All,
ContractResolver = UntypedToTypedValueContractResolver.Instance,
Converters = new [] { new StringEnumConverter() },
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full,
var json = JsonConvert.SerializeObject(dict, Formatting.Indented, settings);
var dict2 = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);
foreach (var pair in dict)
if (!dict2.TryGetValue(pair.Key, out value2))
throw new InvalidOperationException();
if (pair.Value.GetTypeSafe() != value2.GetTypeSafe())
throw new InvalidOperationException();
if (value2 is IConvertible &&
!EqualityComparer<object>.Default.Equals(pair.Value, value2))
throw new InvalidOperationException();
Console.WriteLine("dict deserialized successfully, all values have the same type.");
public static void Main()
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);