web-dev-qa-db-fra.com

comment convertir NameValueCollection en chaîne JSON?

J'ai essayé:

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

elle retourne: ["foo"] Je m'attendais à {"foo" : "baa"} Comment puis-je faire cela?

39
The Mask

NameValueCollection n'est pas un IDictionary, donc JavaScriptSerializer ne peut pas le sérialiser comme vous le souhaitez directement. Vous devrez d'abord le convertir en dictionnaire, puis le sérialiser.

Update : après les questions concernant plusieurs valeurs par clé, l’appel de nvc[key] les renverra simplement, séparées par une virgule, ce qui peut être acceptable. Sinon, on peut toujours appeler GetValues et décider quoi faire avec les valeurs de manière appropriée. Mise à jour du code ci-dessous pour indiquer une possibilité.

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();
        }
    }
}
37
carlosfigueira

Un moyen de sérialiser NameValueCollection consiste tout d'abord à le convertir en dictionnaire, puis à sérialiser le dictionnaire. Pour convertir en dictionnaire:

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

Si vous devez effectuer la conversion fréquemment, vous pouvez également créer une méthode d'extension à NameValueCollection:

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

afin que vous puissiez faire la conversion en une seule ligne comme ceci:

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

var dict = Data.ToDictionary();

Ensuite, vous pouvez sérialiser le dictionnaire:

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

Mais NameValueCollection peut avoir plusieurs valeurs pour une clé, par exemple:

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

Si vous sérialisez cela, vous obtiendrez {"Foo":"baa,again?"}.

Vous pouvez modifier le convertisseur pour produire IDictionary<string, string[]> à la place:

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

Vous pouvez donc obtenir une valeur sérialisée comme ceci: {"Foo":["baa","again?"]}.

38
Endy Tjahjono

Si votre dictionnaire ne doit pas contenir beaucoup d'entrées, vous pouvez utiliser la classe: System.Collections.Specialized.ListDictionary

3
groch

Par souci d'exhaustivité, et parce que la question continue à être posée (par exemple ici ), tant que vous utilisez Json.NET ou DataContractJsonSerializer (mais pas JavaScriptSerializer), vous pouvez utiliser le pattern adapter } et placez la variable NameValueCollection dans un adaptateur IDictionary<string, string[]>, puis sérialisez-la à l'aide de tout sérialiseur prenant totalement en charge la sérialisation de dictionnaires arbitraires.

Une fois cet adaptateur est comme suit:

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
}

Ensuite, une adaptation peut être construite pour un NameValueCollection Data donné simplement en faisant:

var adapter = Data.ToDictionaryAdapter();

Remarques:

  • L'utilisation de l'adaptateur peut s'avérer plus efficace que la simple création d'un dictionnaire copié et devrait bien fonctionner avec tout sérialiseur prenant en charge la sérialisation de dictionnaire.

    L'adaptateur peut également être utile pour utiliser une variable NameValueCollection avec tout autre code qui attend une variable IDictionary - c'est l'avantage fondamental du modèle d'adaptateur.

  • Cela dit, JavaScriptSerializer ne peut pas être utilisé avec l'adaptateur, car ce sérialiseur ne peut pas sérialiser un type arbitraire implémentant IDictionary<TKey, TValue> qui n'hérite pas également de Dictionary<TKey, TValue>. Pour plus de détails, voir S&EACUTE;RIALISATION DES DICTIONNAIRES AVEC JAVASCRIPTSERIALIZER.

  • Lorsque vous utilisez DataContractJsonSerializer, vous pouvez remplacer une NameValueCollection par un adaptateur dans le graphe de sérialisation à l'aide du mécanisme substitut du contrat de données .

  • Lorsque vous utilisez Json.NET, une NameValueCollection peut être remplacée par un adaptateur utilisant un custom JsonConverter tel que:

    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;
        }
    }
    

    Ce qui pourrait être utilisé par exemple. comme suit:

    string json = JsonConvert.SerializeObject(Data, Formatting.Indented, new NameValueJsonConverter<NameValueCollection>());
    
  • NameValueCollection prend en charge tous les éléments suivants 

    • Une valeur null pour une clé donnée;
    • Valeurs multiples pour une clé donnée (auquel cas NameValueCollection.Item[String] renvoie une liste de valeurs séparées par des virgules);
    • Une valeur unique contenant une virgule incorporée (qui ne peut pas être distinguée du cas de plusieurs valeurs lors de l'utilisation de NameValueCollection.Item[String]).


    Ainsi, l'adaptateur doit implémenter IDictionary<string, string[]> plutôt que IDictionary<string, string> et prendre également soin de gérer un tableau de valeurs null.

Exemple de violon (y compris des tests unitaires de base) ici: https://dotnetfiddle.net/gVPSi7 } _

0
dbc