как преобразовать NameValueCollection в строку JSON?

Я старался:

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

он возвращает: ["foo"] Я ожидал {"foo" : "baa"} Как мне это сделать?


person The Mask    schedule 09.08.2011    source источник


Ответы (4)


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();
        }
    }
}
person carlosfigueira    schedule 09.08.2011
comment
NameValueCollection может иметь несколько значений для каждого ключа, это не решает эту проблему. - person zzzzBov; 16.04.2013
comment
Этот метод, скорее всего, вызовет исключения! Причина, по которой существует NameValueCollection (и .NET иногда не использует словарь), заключается в том, чтобы разрешить несколько значений для каждого ключа. Для словаря требуется один и только один ключ для каждого значения. - person John Culviner; 08.07.2013
comment
Метод не будет генерировать исключение. Если есть несколько ключей, nvc[key] просто вернет их, соединенные символом ,. Но для полноты картины я обновил ответ, чтобы он был более подробным. - person carlosfigueira; 08.07.2013
comment
Если строка запроса не была преобразована в NameValueCollection, это работает: string" title="как преобразовать строку запроса в строку json"> stackoverflow.com/questions/12428947/ - person sobelito; 30.08.2013
comment
DotNetFiddle: Несколько ключей в NameValueCollection dotnetfiddle.net/hnYwGn - person Rodrigo Groener; 23.12.2015

Один из способов сериализации NameValueCollection — сначала преобразовать его в словарь, а затем сериализовать словарь. Чтобы преобразовать в словарь:

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?"]}.

person Endy Tjahjono    schedule 16.03.2013

Если ваш словарь не предназначен для большого количества статей, вы можете использовать класс: System.Collections.Specialized.ListDictionary

person groch    schedule 22.11.2011

Для полноты картины и поскольку вопрос продолжает задаваться (например, здесь), пока вы используя Json.NET или DataContractJsonSerializer (но не 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();

Примечания:

Пример скрипта (включая базовое модульное тестирование) здесь: https://dotnetfiddle.net/gVPSi7

person dbc    schedule 09.10.2018