[英]how to convert NameValueCollection to JSON string?
我試過了:
NameValueCollection Data = new NameValueCollection();
Data.Add("foo","baa");
string json = new JavaScriptSerializer().Serialize(Data);
它返回: ["foo"]
我期望{"foo": "baa"}
我該怎么做?
序列化 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?"]}
。
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();
}
}
}
如果您的字典不打算包含許多條目,則可以使用 class: System.Collections.Specialized.ListDictionary
為了完整起見,並且由於繼續詢問問題(例如此處),只要您使用 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();
筆記:
使用適配器可能比簡單地創建一個復制的字典更高效,並且應該與任何完全支持字典序列化的序列化程序一起工作。
在將NameValueCollection
與任何其他需要某種IDictionary
的代碼一起使用時,適配器也可能很有用——這是適配器模式的基本優勢。
話雖如此, JavaScriptSerializer
不能與適配器一起使用,因為此序列化程序無法序列化實現IDictionary<TKey, TValue>
的任意類型,該類型也不繼承自Dictionary<TKey, TValue>
。 有關詳細信息,請參閱使用 JavaScriptSerializer 序列化字典。
使用DataContractJsonSerializer
時,可以使用數據協定代理機制將NameValueCollection
替換為序列化圖中的適配器。
使用 Json.NET 時,可以使用自定義JsonConverter
將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
支持以下所有
null
值;NameValueCollection.Item[String]
返回以逗號分隔的值列表);NameValueCollection.Item[String]
時無法與多個值的情況區分開來)。
因此適配器必須實現IDictionary<string, string[]>
而不是IDictionary<string, string>
並且還要注意處理null
值數組。
小提琴示例(包括一些基本單元測試): https://dotnetfiddle.net/gVPSi7
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.