web-dev-qa-db-ger.com

wie konvertiert man NameValueCollection in JSON-String?

Ich habe es versucht:

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

es gibt zurück: ["foo"] Ich erwartete {"foo" : "baa"} Wie mache ich das?

39
The Mask

Da NameValueCollection kein IDictionary ist, kann die JavaScriptSerializer es nicht wie erwartet direkt serialisieren. Sie müssen es zuerst in ein Wörterbuch konvertieren und dann serialisieren.

Update : Bei Fragen zu mehreren Werten pro Schlüssel gibt der Aufruf von nvc[key] sie einfach durch ein Komma getrennt zurück, was in Ordnung sein kann. Wenn nicht, kann man immer GetValues aufrufen und entsprechend entscheiden, was mit den Werten zu tun ist. Der folgende Code wurde aktualisiert, um einen möglichen Weg zu zeigen.

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

Eine Möglichkeit, NameValueCollection zu serialisieren, besteht darin, sie zuerst in Dictionary zu konvertieren und dann das Dictionary zu serialisieren. So konvertieren Sie in ein Wörterbuch:

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

Wenn Sie die Konvertierung häufig durchführen müssen, können Sie auch eine Erweiterungsmethode für NameValueCollection erstellen:

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

so können Sie die Konvertierung in einer Zeile wie folgt durchführen:

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

var dict = Data.ToDictionary();

Dann können Sie das Wörterbuch serialisieren:

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

NameValueCollection kann jedoch mehrere Werte für einen Schlüssel enthalten, beispielsweise:

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

Wenn Sie dies serialisieren, erhalten Sie {"Foo":"baa,again?"}.

Sie können den Konverter ändern, um stattdessen IDictionary<string, string[]> zu erzeugen:

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

Sie können also einen serialisierten Wert wie folgt erhalten: {"Foo":["baa","again?"]}.

38
Endy Tjahjono

Wenn Ihr Wörterbuch nicht viele Einträge enthalten soll, können Sie die Klasse verwenden: System.Collections.Specialized.ListDictionary

3
groch

Der Vollständigkeit halber, und weil die Frage weiterhin gestellt wird (z. B. here ), , solange Sie Json.NET oder DataContractJsonSerializer (aber nicht JavaScriptSerializer) verwenden, können Sie den Adapter verwenden pattern und wickeln Sie die Variable NameValueCollection in einen IDictionary<string, string[]>-Adapter ein, und serialisieren Sie diesen mit einem Serializer, der die Serialisierung von beliebigen Wörterbüchern unterstützt.

Einmal ist ein solcher Adapter wie folgt:

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
}

Dann kann ein angepasster Code für einen bestimmten NameValueCollection Data einfach erstellt werden:

var adapter = Data.ToDictionaryAdapter();

Anmerkungen:

  • Die Verwendung des Adapters ist möglicherweise leistungsfähiger als das Erstellen eines kopierten Wörterbuchs und sollte mit jedem Serialisierer funktionieren, der die Wörterbuch-Serialisierung vollständig unterstützt.

    Der Adapter kann auch nützlich sein, wenn Sie eine NameValueCollection mit einem anderen Code verwenden, der eine IDictionary von irgendeiner Art erwartet - dies ist der grundlegende Vorteil des Adaptermusters.

  • JavaScriptSerializer kann jedoch nicht mit dem Adapter verwendet werden, da dieser Serialisierer keinen beliebigen Typ serialisieren kann, der IDictionary<TKey, TValue> implementiert und nicht auch von Dictionary<TKey, TValue> erbt. Weitere Informationen finden Sie unter Serialisierung von Wörterbüchern mit JavaScriptSerializer.

  • Bei Verwendung von DataContractJsonSerializer kann eine NameValueCollection durch einen Adapter in der Serialisierungsgrafik ersetzt werden, indem der data contract-Surrogat Mechanismus verwendet wird.

  • Bei Verwendung von Json.NET kann eine NameValueCollection durch einen Adapter ersetzt werden, indem eine custom JsonConverter wie folgt verwendet wird:

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

    Welches verwendet werden könnte, z.B. wie folgt:

    string json = JsonConvert.SerializeObject(Data, Formatting.Indented, new NameValueJsonConverter<NameValueCollection>());
    
  • NameValueCollection unterstützt Folgendes: 

    • Ein null-Wert für einen bestimmten Schlüssel;
    • Mehrere Werte für einen bestimmten Schlüssel (in diesem Fall gibt NameValueCollection.Item[String] eine durch Kommas getrennte Liste von Werten zurück);
    • Ein einzelner Wert, der ein eingebettetes Komma enthält (das bei Verwendung von NameValueCollection.Item[String] nicht von mehreren Werten unterschieden werden kann).


    Daher muss der Adapter IDictionary<string, string[]> anstelle von IDictionary<string, string> implementieren und darauf achten, ein null-Wertefeld zu behandeln.

Beispiel für eine Geige (einschließlich einiger Grundeinheitentests) hier: https://dotnetfiddle.net/gVPSi7

0
dbc