41

Trying to copy values from an existing NameValueCollection object to a Dictionary. I have the following code below to do that but seems the Add does not accept that my keys and values are as Strings

IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
public void copyFromNameValueCollection (NameValueCollection a)
{
    foreach (var k in a.AllKeys)
    { 
        dict.Add(k, a[k]);
    }  
}

Note: NameValueCollection contains String keys and values and so I simply want to provide here a method to allow copying of those to a generic dictionary.

Kobojunkie
  • 6,375
  • 31
  • 109
  • 164
  • 2
    It's very confusing, because presumably this is in a generic *type* declaring `TKey`, `TValue` type parameters - and your *method* declares those type parameters too. Add to that the fact that the keys will always be strings... so what do you expect to happen if TKey is (say) Guid? – Jon Skeet May 14 '13 at 18:00
  • I need a generic dictionary to work with. Please note that this is in building a serializable dictionary class, that also allows for copying of keys and values from NameValueCollection objects – Kobojunkie May 14 '13 at 18:04
  • 1
    Again, what would this do if TKey isn't string? – Jon Skeet May 14 '13 at 18:05

7 Answers7

82

Extension method plus linq:

 public static Dictionary<string, string> ToDictionary(this NameValueCollection nvc) {
    return nvc.AllKeys.ToDictionary(k => k, k => nvc[k]);
 }

 //example
 var dictionary = nvc.ToDictionary();
katbyte
  • 2,665
  • 2
  • 28
  • 19
39

It doesn't make sense to use generics here since you can't assign strings to some arbitrary generic type:

IDictionary<string, string> dict = new Dictionary<string, string>();

public void copyFrom(NameValueCollection a)
{
            foreach (var k in a.AllKeys)
            { 
                dict.Add(k, a[k]);
            }  
}

although you should probably create a method to create a new dictionary instead:

public static IDictionary<string, string> ToDictionary(this NameValueCollection col)
{
    IDictionary<string, string> dict = new Dictionary<string, string>();
    foreach (var k in col.AllKeys)
    { 
        dict.Add(k, col[k]);
    }  
    return dict;
}

which you can use like:

NameValueCollection nvc = //
var dictionary = nvc.ToDictionary();

If you want a general way of converting the strings in the collection into the required key/value types, you can use type converters:

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this NameValueCollection col)
{
    var dict = new Dictionary<TKey, TValue>();
    var keyConverter = TypeDescriptor.GetConverter(typeof(TKey));
    var valueConverter = TypeDescriptor.GetConverter(typeof(TValue));

    foreach(string name in col)
    {
        TKey key = (TKey)keyConverter.ConvertFromString(name);
        TValue value = (TValue)valueConverter.ConvertFromString(col[name]);
        dict.Add(key, value);
    }

    return dict;
}
Jon
  • 4,925
  • 3
  • 28
  • 38
Lee
  • 142,018
  • 20
  • 234
  • 287
  • NameValueCollection has String keys and values. I want to be able to copy those over to a generic dictionary. – Kobojunkie May 14 '13 at 18:03
  • @Kobojunkie - You can't copy it to a generic dictionary, since the key and value types can be anything. You can only copy it to a `Dictionary` or one with less-derived key or value types e.g. `object`. – Lee May 14 '13 at 18:05
  • @Kobojunkie - Of course it's correct - you can't say `T obj = "abc"` for **any** type `T`. It will only compile if `T` is `string` or a less-derived type. You could convert your strings to the target type, but that isn't clear from your question. – Lee May 14 '13 at 18:10
  • @Kobojunkie - I've added a general way of converting the keys/values in the collection into the target types. – Lee May 14 '13 at 18:15
  • I thought you said earlier that it was not possible? – Kobojunkie May 14 '13 at 18:25
  • @Kobojunkie - It's not possible without converting the strings. The code in your question is trying to insert strings into a dictionary with unknown key and value types, which is not possible. – Lee May 14 '13 at 18:28
  • This answer doesn't quite work if NameValueCollection has a null key - which is perfectly acceptable according to the NameValueCollection documentation. Before inserting key into Dictionary check if null and insert empty string I guess? – xgalaxy Oct 01 '15 at 17:44
25
parameters.AllKeys.ToDictionary(t => t, t => parameters[t]);
Chobits
  • 275
  • 2
  • 14
  • 32
5

Use LINQ:

public static IDictionary<string, string> ToDictionary(this NameValueCollection collection)
{
    return collection.Cast<string>().ToDictionary(k => k, v => collection[v]);
}

Usage:

IDictionary<string, string> dic = nv.ToDictionary();
abatishchev
  • 98,240
  • 88
  • 296
  • 433
3

Super-Short Version

var dataNvc = HttpUtility.ParseQueryString(data);
var dataCollection = dataNvc.AllKeys.ToDictionary(o => o, o => dataNvc[o]);
1232133d2ffa
  • 191
  • 1
  • 13
2

If you know that your dictionary is always going to contain strings, specify it to contain strings instead of making your class generic:

IDictionary<string, string> dict = new Dictionary<string, string>();

With this, things will "just work" as written (without the generic method specification).

If you need this to be a generic class, and hold generic data, you need some way to convert from string to TKey and string to TValue. You could provide delegates to your copy method to do this:

public void CopyFrom(NameValueCollection a, Func<string, TKey> keyConvert, Func<string, TValue> valueConvert)
{
    foreach(var k in a.AllKeys)
    {
         dict.Add(keyConvert(k), valueConvert(a[k]));
    }
}

You would then need to pass a delegate in that would perform the conversion from string to TValue and string to TKey.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 1
    This answer should mention that `NameValueCollection`s can have null keys, and this mismatch must be accounted for in converting to a dictionary, which has a non null key invariant – smartcaveman May 14 '13 at 18:05
  • The Dictionary is not always going to contain strings. I simply want it to also be able to copy values from NameValueCollection object in this method. – Kobojunkie May 14 '13 at 18:05
  • 1
    @Kobojunkie Then you need the second mechanism - You need to convert from string to your types – Reed Copsey May 14 '13 at 18:06
  • That is what I am seeking help with here. How do I convert the string keys and values in the NameValueCollection to the generic type in this method so I can load those to my generic dictionary object – Kobojunkie May 14 '13 at 18:08
  • 1
    @Kobojunkie There is no "general purpose" way to do this - you need some type of method to do the conversion. My second option shows how to write the method so this can be passed in as a delegate. – Reed Copsey May 14 '13 at 18:11
  • Again, the reason for this question is so I can get help with that "option". I need a way to, in my generic Dictionary class, provide a way for copying of data from NameValueCollection objects as well. That is what I am trying to find here. – Kobojunkie May 14 '13 at 18:14
0

You should not forget about EqualityComparer. But it is not a public property. So, you should use reflection to get it.

public static IEqualityComparer GetEqualityComparer(this NameObjectCollectionBase nameObjectCollection)
  {
  PropertyInfo propertyInfo = typeof(NameObjectCollectionBase).GetProperty("Comparer", BindingFlags.Instance | BindingFlags.NonPublic);
  return (IEqualityComparer)propertyInfo.GetValue(nameObjectCollection);
  }

public static IEqualityComparer<string> GetEqualityComparer(this NameValueCollection nameValueCollection)
  {
  return (IEqualityComparer<string>)((NameObjectCollectionBase)nameValueCollection).GetEqualityComparer();
  }

public static Dictionary<string, string> ToDictionary(this NameValueCollection nameValueCollection)
  {
  Dictionary<string, string> dictionary =
    nameValueCollection.AllKeys.ToDictionary(x => x, x => nameValueCollection[x], nameValueCollection.GetEqualityComparer());
  return dictionary;
  }
Dmitriy Dokshin
  • 710
  • 5
  • 25