如何将NameValueCollection转换为JSON字符串?

时间:2011-08-09 22:19:48

标签: c# .net asp.net json namevaluecollection

我试过了:

  NameValueCollection Data = new NameValueCollection();
  Data.Add("foo","baa");
  string json = new JavaScriptSerializer().Serialize(Data);

它会返回:["foo"]我期待{"foo" : "baa"} 我该怎么做?

4 个答案:

答案 0 :(得分:38)

NameValueCollection不是IDictionary,因此JavaScriptSerializer无法按预期直接序列化它。您需要先将其转换为字典,然后将其序列化。

更新:关于每个键的多个值的问题,对nvc[key]的调用只会以逗号分隔它们,这可能没问题。如果没有,可以随时调用GetValues并确定如何正确处理这些值。更新了以下代码,以显示一种可能的方式。

public class StackOverflow_7003740
{
    static Dictionary<string, object> NvcToDictionary(NameValueCollection nvc, bool handleMultipleValuesPerKey)
    {
        var result = new Dictionary<string, object>();
        foreach (string key in nvc.Keys)
        {
            if (handleMultipleValuesPerKey)
            {
                string[] values = nvc.GetValues(key);
                if (values.Length == 1)
                {
                    result.Add(key, values[0]);
                }
                else
                {
                    result.Add(key, values);
                }
            }
            else
            {
                result.Add(key, nvc[key]);
            }
        }

        return result;
    }

    public static void Test()
    {
        NameValueCollection nvc = new NameValueCollection();
        nvc.Add("foo", "bar");
        nvc.Add("multiple", "first");
        nvc.Add("multiple", "second");

        foreach (var handleMultipleValuesPerKey in new bool[] { false, true })
        {
            if (handleMultipleValuesPerKey)
            {
                Console.WriteLine("Using special handling for multiple values per key");
            }
            var dict = NvcToDictionary(nvc, handleMultipleValuesPerKey);
            string json = new JavaScriptSerializer().Serialize(dict);
            Console.WriteLine(json);
            Console.WriteLine();
        }
    }
}

答案 1 :(得分:38)

序列化NameValueCollection的一种方法是首先将其转换为Dictionary,然后序列化Dictionary。要转换为字典:

thenvc.AllKeys.ToDictionary(k => k, k => thenvc[k]);

如果您需要经常进行转换,您还可以为NameValueCollection创建扩展方法:

public static class NVCExtender
{
    public static IDictionary<string, string> ToDictionary(
                                        this NameValueCollection source)
    {
        return source.AllKeys.ToDictionary(k => k, k => source[k]);
    }
}

所以你可以像这样在一行进行转换:

NameValueCollection Data = new NameValueCollection();
Data.Add("Foo", "baa");

var dict = Data.ToDictionary();

然后你可以序列化字典:

var json = new JavaScriptSerializer().Serialize(dict);
// you get {"Foo":"baa"}

但NameValueCollection可以为一个键设置多个值,例如:

NameValueCollection Data = new NameValueCollection();
Data.Add("Foo", "baa");
Data.Add("Foo", "again?");

如果您序列化,则会获得{"Foo":"baa,again?"}

您可以修改转换器以生成IDictionary<string, string[]>

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

因此,您可以获得如下序列化值:{"Foo":["baa","again?"]}

答案 2 :(得分:3)

如果您的词典不包含许多条目,则可以使用该类: System.Collections.Specialized.ListDictionary

答案 3 :(得分:0)

出于完整性的考虑,并且因为问题继续被问到(例如here),所以只要您使用的是Json.NET或DataContractJsonSerializer(而不是{{1} }),您可以使用adapter pattern并将JavaScriptSerializer包装在NameValueCollection适配器中,然后使用完全支持序列化任意字典的任何序列化程序对其进行序列化。

一旦这样的适配器如下:

IDictionary<string, string[]>

然后只需执行以下操作即可为给定的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; } // Method instead of a property to guarantee that nobody tries to serialize it. 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); if (value == null) collection.Add(key, null); else 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); if (found) collection.Remove(key); return found; } public bool TryGetValue(string key, out string[] value) { return (value = collection.GetValues(key)) != null; } public ICollection<string[]> Values { get { return new ReadOnlyCollectionAdapter<KeyValuePair<string, string[]>, string[]>(this, p => p.Value); } } public string[] this[string key] { get { var value = collection.GetValues(key); if (value == null) throw new KeyNotFoundException(key); return value; } set { Remove(key); Add(key, value); } } #endregion #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) { string[] value; if (!TryGetValue(item.Key, out value)) return false; return EqualityComparer<string[]>.Default.Equals(item.Value, value); // Consistent with Dictionary<TKey, TValue> } 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) { if (Contains(item)) return Remove(item.Key); return false; } #endregion #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)); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } public static class NameValueCollectionExtensions { public static NameValueCollectionDictionaryAdapter<TNameValueCollection> ToDictionaryAdapter<TNameValueCollection>(this TNameValueCollection collection) where TNameValueCollection : NameValueCollection, new() { if (collection == null) 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; this.toOuter = toOuter; } 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))) return true; return false; } 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); #endregion #region IEnumerable<TOut> Members public IEnumerator<TOut> GetEnumerator() { foreach (var item in Collection) yield return ToOuter(item); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } 构造一个适配子:

NameValueCollection Data

注意:

  • 使用适配器可能比仅创建复制的字典更有效,并且应与完全支持字典序列化的任何序列化程序一起很好地工作。

    在将var adapter = Data.ToDictionaryAdapter(); 与期望使用NameValueCollection的任何其他代码一起使用时,适配器也可能很有用-这是适配器模式的基本优点。

  • 话虽如此,IDictionary不能与适配器一起使用,因为此序列化程序无法序列化也不能继承自JavaScriptSerializer的实现IDictionary<TKey, TValue>的任意类型。有关详细信息,请参见 Serializing dictionaries with JavaScriptSerializer

  • 使用Dictionary<TKey, TValue>时,可以使用data contract surrogate机制在序列化图中用适配器替换DataContractJsonSerializer

  • 使用Json.NET时,可以使用custom JsonConverterNameValueCollection替换为适配器,如下所示:

    NameValueCollection

    例如可以使用如下:

    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)
                return null;
    
            // Reuse the existing NameValueCollection if present
            var collection = (TNameValueCollection)existingValue ?? new TNameValueCollection();
            var dictionaryWrapper = collection.ToDictionaryAdapter();
    
            serializer.Populate(reader, dictionaryWrapper);
    
            return collection;
        }
    
        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())
                ;
            return reader;
        }
    }
    
  • string json = JsonConvert.SerializeObject(Data, Formatting.Indented, new NameValueJsonConverter<NameValueCollection>()); 支持以下所有

    • 给定键的NameValueCollection值;
    • 给定键的多个值(在这种情况下,NameValueCollection.Item[String]返回以逗号分隔的值列表);
    • 包含嵌入式逗号的单个值(使用null时不能与多个值区分)。


    ,因此适配器必须实现NameValueCollection.Item[String]而不是IDictionary<string, string[]>,并且还要小​​心处理IDictionary<string, string>值数组。

此处提供样本小提琴(包括一些基本的单元测试):https://dotnetfiddle.net/gVPSi7