using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.Specialized;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class NameValueCollectionDictionaryAdapter<TNameValueCollection> : IDictionary<string, string[]>
where TNameValueCollection : NameValueCollection, new()
readonly TNameValueCollection collection;
public NameValueCollectionDictionaryAdapter() : this(new TNameValueCollection()) { }
public NameValueCollectionDictionaryAdapter(TNameValueCollection collection)
this.collection = collection;
public TNameValueCollection GetCollection() { return collection; }
#region IDictionary<string,string[]> Members
public void Add(string key, string[] value)
if (collection.GetValues(key) != null)
throw new ArgumentException("Duplicate key " + key);
collection.Add(key, null);
foreach (var str in value)
collection.Add(key, str);
public bool ContainsKey(string key) { return collection.GetValues(key) != null; }
public ICollection<string> Keys { get { return collection.AllKeys; } }
public bool Remove(string key)
bool found = ContainsKey(key);
public bool TryGetValue(string key, out string[] value)
return (value = collection.GetValues(key)) != null;
public ICollection<string[]> Values
return new ReadOnlyCollectionAdapter<KeyValuePair<string, string[]>, string[]>(this, p => p.Value);
public string[] this[string key]
var value = collection.GetValues(key);
throw new KeyNotFoundException(key);
#region ICollection<KeyValuePair<string,string[]>> Members
public void Add(KeyValuePair<string, string[]> item) { Add(item.Key, item.Value); }
public void Clear() { collection.Clear(); }
public bool Contains(KeyValuePair<string, string[]> item)
if (!TryGetValue(item.Key, out value))
return EqualityComparer<string[]>.Default.Equals(item.Value, value);
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
foreach (var item in this)
array[arrayIndex++] = item;
public int Count { get { return collection.Count; } }
public bool IsReadOnly { get { return false; } }
public bool Remove(KeyValuePair<string, string[]> item)
#region IEnumerable<KeyValuePair<string,string[]>> Members
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
foreach (string key in collection)
yield return new KeyValuePair<string, string[]>(key, collection.GetValues(key));
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
public static class NameValueCollectionExtensions
public static NameValueCollectionDictionaryAdapter<TNameValueCollection> ToDictionaryAdapter<TNameValueCollection>(this TNameValueCollection collection)
where TNameValueCollection : NameValueCollection, new()
throw new ArgumentNullException();
return new NameValueCollectionDictionaryAdapter<TNameValueCollection>(collection);
public class ReadOnlyCollectionAdapter<TIn, TOut> : CollectionAdapterBase<TIn, TOut, ICollection<TIn>>
public ReadOnlyCollectionAdapter(ICollection<TIn> collection, Func<TIn, TOut> toOuter)
: base(() => collection, toOuter)
public override void Add(TOut item) { throw new NotImplementedException(); }
public override void Clear() { throw new NotImplementedException(); }
public override bool IsReadOnly { get { return true; } }
public override bool Remove(TOut item) { throw new NotImplementedException(); }
public abstract class CollectionAdapterBase<TIn, TOut, TCollection> : ICollection<TOut>
where TCollection : ICollection<TIn>
readonly Func<TCollection> getCollection;
readonly Func<TIn, TOut> toOuter;
public CollectionAdapterBase(Func<TCollection> getCollection, Func<TIn, TOut> toOuter)
if (getCollection == null || toOuter == null)
throw new ArgumentNullException();
this.getCollection = getCollection;
protected TCollection Collection { get { return getCollection(); } }
protected TOut ToOuter(TIn inner) { return toOuter(inner); }
#region ICollection<TOut> Members
public abstract void Add(TOut item);
public abstract void Clear();
public virtual bool Contains(TOut item)
var comparer = EqualityComparer<TOut>.Default;
foreach (var member in Collection)
if (comparer.Equals(item, ToOuter(member)))
public void CopyTo(TOut[] array, int arrayIndex)
foreach (var item in this)
array[arrayIndex++] = item;
public int Count { get { return Collection.Count; } }
public abstract bool IsReadOnly { get; }
public abstract bool Remove(TOut item);
#region IEnumerable<TOut> Members
public IEnumerator<TOut> GetEnumerator()
foreach (var item in Collection)
yield return ToOuter(item);
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public class NameValueJsonConverter<TNameValueCollection> : JsonConverter
where TNameValueCollection : NameValueCollection, new()
public override bool CanConvert(Type objectType)
return typeof(TNameValueCollection).IsAssignableFrom(objectType);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.SkipComments().TokenType == JsonToken.Null)
var collection = (TNameValueCollection)existingValue ?? new TNameValueCollection();
var dictionaryWrapper = collection.ToDictionaryAdapter();
serializer.Populate(reader, dictionaryWrapper);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var collection = (TNameValueCollection)value;
var dictionaryWrapper = new NameValueCollectionDictionaryAdapter<TNameValueCollection>(collection);
serializer.Serialize(writer, dictionaryWrapper);
public static partial class JsonExtensions
public static JsonReader SkipComments(this JsonReader reader)
while (reader.TokenType == JsonToken.Comment && reader.Read())
abstract class NameValueCollectionDictionaryWrapperTesterBase
protected abstract string Serialize(NameValueCollection value);
protected abstract NameValueCollection Deserialize(string json);
Assert.IsTrue(!new NameValueCollectionOrderedComparer().Equals(data1, data2));
Assert.IsTrue(!new NameValueCollectionOrderedComparer().Equals(data2, data3));
Assert.IsTrue(!new NameValueCollectionOrderedComparer().Equals(data1, data3));
Console.WriteLine("\n{0}: All tests passed.", GetType());
NameValueCollection Test1()
var data = new NameValueCollection();
data.Add("multiple", "first");
data.Add("multiple", "second,third,fourth");
data.Add("nullMultiple", null);
data.Add("nullMultiple", null);
data.Add("nullMixed", null);
data.Add("nullMixed", "aaa");
data.Add("nullMixed", null);
var data2 = TestRoundTrip(data);
NameValueCollection Test2()
var data = new NameValueCollection();
data.Add("multiple", "first");
data.Add("multiple", "second,third");
data.Add("multiple", "fourth");
data.Add("nullMultiple", null);
data.Add("nullMultiple", null);
data.Add("nullMixed", null);
data.Add("nullMixed", "aaa");
data.Add("nullMixed", null);
var data2 = TestRoundTrip(data);
NameValueCollection Test3()
var data = new NameValueCollection();
var data2 = TestRoundTrip(data);
private NameValueCollection TestRoundTrip(NameValueCollection data)
var json = Serialize(data);
Console.WriteLine("\nTesting JSON: ");
var data2 = Deserialize(json);
var json2 = Serialize(data2);
Console.WriteLine("Round-tripped result: ");
Console.WriteLine(json2);
Assert.IsTrue(new NameValueCollectionOrderedComparer().Equals(data, data2));
class NameValueCollectionDictionaryWrapperJsonNetTester : NameValueCollectionDictionaryWrapperTesterBase
protected override string Serialize(NameValueCollection value)
return JsonConvert.SerializeObject(value, Formatting.Indented, new NameValueJsonConverter<NameValueCollection>());
protected override NameValueCollection Deserialize(string json)
return JsonConvert.DeserializeObject<NameValueCollection>(json, new NameValueJsonConverter<NameValueCollection>());
public static void Test()
new NameValueCollectionDictionaryWrapperJsonNetTester().Test();
Console.WriteLine("\nDone testing.");
public class NameValueCollectionOrderedComparer : IEqualityComparer<NameValueCollection>
#region IEqualityComparer<NameValueCollection> Members
public bool Equals(NameValueCollection x, NameValueCollection y)
if (object.ReferenceEquals(x, y))
else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
if (!x.Keys.Cast<string>().SequenceEqual(y.Keys.Cast<string>()))
foreach (string key in x.Keys)
var xValues = x.GetValues(key);
var yValues = y.GetValues(key);
if (xValues == null && yValues != null)
else if (xValues != null && yValues == null)
else if (xValues != null && yValues != null && !xValues.SequenceEqual(yValues))
public int GetHashCode(NameValueCollection obj)
return obj.Count.GetHashCode();
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: ");