using System.Collections.Generic;
using System.Diagnostics;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class KeyValueConverter : JsonConverter
interface IToKeyValuePair
struct Pair<TKey, TValue> : IToKeyValuePair
public TKey Key { get; set; }
public TValue Value { get; set; }
public object ToKeyValuePair()
return new KeyValuePair<TKey, TValue>(Key, Value);
public override bool CanConvert(Type objectType)
bool isNullable = (Nullable.GetUnderlyingType(objectType) != null);
Type type = (Nullable.GetUnderlyingType(objectType) ?? objectType);
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
public override bool CanWrite { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
bool isNullable = (Nullable.GetUnderlyingType(objectType) != null);
Type type = (Nullable.GetUnderlyingType(objectType) ?? objectType);
if (isNullable && reader.TokenType == JsonToken.Null)
&& type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
var pairType = typeof(Pair<,>).MakeGenericType(type.GetGenericArguments());
var pair = serializer.Deserialize(reader, pairType);
return ((IToKeyValuePair)pair).ToKeyValuePair();
throw new JsonSerializationException("Invalid type: " + objectType);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
public static void Test()
var settings = new JsonSerializerSettings
MissingMemberHandling = MissingMemberHandling.Error,
Converters = new JsonConverter[] { new KeyValueConverter() },
TestInvalidJson(settings);
private static void TestValidJson(JsonSerializerSettings settings)
Console.WriteLine("Testing valid JSON...");
var dict = new Dictionary<string, string> { { "a", "aaaa" } };
var dictJson = JsonConvert.SerializeObject(dict, settings);
var dict2 = JsonConvert.DeserializeObject<Dictionary<string, string>>(dictJson, settings);
if (!(dict.Count == dict2.Count && !dict.Except(dict2).Any()))
Debug.Assert(false, "Dictionaries not equal");
throw new InvalidOperationException();
var jsonFixed = JsonConvert.SerializeObject(dict.ToList());
var listFixed = JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>(jsonFixed, settings);
Console.WriteLine(JsonConvert.SerializeObject(listFixed, settings));
var arrayFixed = JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>(jsonFixed, settings);
Console.WriteLine(JsonConvert.SerializeObject(arrayFixed, settings));
var jsonFixed2 = JsonConvert.SerializeObject(dict.Select(p => (object)p).Concat(new object[] { null }));
var listFixed2 = JsonConvert.DeserializeObject<List<KeyValuePair<string, object>?>>(jsonFixed2, settings);
Console.WriteLine(JsonConvert.SerializeObject(listFixed2, settings));
var arrayFixed2 = JsonConvert.DeserializeObject<List<KeyValuePair<string, object>?>>(jsonFixed2, settings);
Console.WriteLine(JsonConvert.SerializeObject(arrayFixed2, settings));
private static void TestInvalidJson(JsonSerializerSettings settings)
Console.WriteLine("Testing invalid JSON...");
string json = @"['a','b']";
string jsonCase2 = @"[{'a':'b','c':'d'}]";
string jsonCase3 = @"[{'Key':{'x':1},'Value':false}]";
TestInvalidJson(jsonCase3, settings);
TestInvalidJson(jsonCase2, settings);
TestInvalidJson(json, settings);
private static void TestInvalidJson(string json, JsonSerializerSettings settings)
var list = JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>(json, settings);
Debug.Assert(false, "Exception should have been thrown");
throw new InvalidOperationException();
Console.WriteLine("Correctly caught exception: " + ex.Message);
var array = JsonConvert.DeserializeObject<KeyValuePair<string, object>[]>(json, settings);
Debug.Assert(false, "Exception should have been thrown");
throw new InvalidOperationException();
Console.WriteLine("Correctly caught exception: " + ex.Message);
public static void Main()