how to convert NameValueCollection to JSON string?
NameValueCollection
isn't an IDictionary, so the JavaScriptSerializer
cannot serialize it as you expect directly. You'll need to first convert it into a dictionary, then serialize it.
Update: following questions regarding multiple values per key, the call to nvc[key]
will simply return them separated by a comma, which may be ok. If not, one can always call GetValues
and decide what to do with the values appropriately. Updated the code below to show one possible way.
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();
}
}
}
Serializing a custom subclass of NameValueCollection with Json.Net
There are a few things going on here:
Json.NET cannot serialize a
NameValueCollection
without a custom converter becauseNameValueCollection
implementsIEnumerable
for iterating over the keys, but does not implementIDictionary
for iterating over keys and values. See this answer for a fuller explanation of why this causes problems for Json.NET.Because
NameValueCollection
implementsIEnumerable
, Json.NET sees your class as a collection, and so serializes it as a JSON array and not a JSON object with named properties. Thus, yourChildren
are not serialized. Again, a custom converter would be required to fix this.Assuming the above issues are resolved, if your
HL7
subclass ofNameValueCollection
happens to have a key named"Children"
you will generate invalid JSON when serializing it, namely an object with duplicated property names. I suggest moving the names & values into a nested property (named, e.g., "Values") for purposes of unambiguous serialization.NameValueCollection
actually can have multiple string values for a given key string, so its entry values need to be serialized as a JSON array not a single string.
Putting all this together, the following code:
[JsonConverter(typeof(HL7Converter))]
public class HL7 : NameValueCollection
{
public List<HL7> Children { get; set; }
public HL7()
{
Children = new List<HL7>();
}
}
public class HL7Converter : JsonConverter
{
class HL7Proxy
{
public NameValueCollectionDictionaryWrapper Values { get; set; }
public List<HL7> Children { get; set; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(HL7);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var proxy = serializer.Deserialize<HL7Proxy>(reader);
if (proxy == null)
return existingValue;
var hl7 = existingValue as HL7;
if (hl7 == null)
hl7 = new HL7();
hl7.Add(proxy.Values.GetCollection());
if (proxy.Children != null)
hl7.Children.AddRange(proxy.Children);
return hl7;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
HL7 hl7 = (HL7)value;
if (hl7 == null)
return;
serializer.Serialize(writer, new HL7Proxy { Children = hl7.Children, Values = new NameValueCollectionDictionaryWrapper(hl7) });
}
}
// Proxy dictionary to serialize & deserialize a NameValueCollection. We use a proxy dictionary rather than a real dictionary because NameValueCollection is an ordered collection but the generic dictionary class is unordered.
public class NameValueCollectionDictionaryWrapper: IDictionary<string, string []>
{
readonly NameValueCollection collection;
public NameValueCollectionDictionaryWrapper()
: this(new NameValueCollection())
{
}
public NameValueCollectionDictionaryWrapper(NameValueCollection collection)
{
this.collection = collection;
}
// Method instead of a property to guarantee that nobody tries to serialize it.
public NameValueCollection 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);
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)
{
value = collection.GetValues(key);
return value != null;
}
public ICollection<string[]> Values
{
get {
return Enumerable.Range(0, collection.Count).Select(i => collection.GetValues(i)).ToArray();
}
}
public string[] this[string key]
{
get
{
var value = collection.GetValues(key);
if (value == null)
throw new KeyNotFoundException();
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
}
Using the following test case:
HL7 hl7 = new HL7();
hl7.Add("a", "123");
hl7.Add("b", "456");
hl7.Add("Children", "Children");
hl7.Children.Add(new HL7());
hl7.Children[0].Add("c", "123");
hl7.Children[0].Add("d", "456");
hl7.Children[0].Add("d", "789");
var json = JsonConvert.SerializeObject(hl7, Formatting.Indented);
Debug.WriteLine(json);
Gives the following JSON:
{
"Values": {
"a": [
"123"
],
"b": [
"456"
],
"Children": [
"Children"
]
},
"Children": [
{
"Values": {
"c": [
"123"
],
"d": [
"456",
"789"
]
},
"Children": []
}
]
}
How to convert json to NameValueCollection
I'm not sure why everyone is still recommending JSON.NET for deserialization of JSON. I wrote a blog post on how to deserialize JSON to C#.
In short, it's like this:
using System.Web.Script.Serialization;
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<Dictionary<string, string>>(jsonText);
NameValueCollection nvc = null;
if (dict != null) {
nvc = new NameValueCollection(dict.Count);
foreach (var k in dict) {
nvc.Add(k.Key, k.Value);
}
}
}
var json = jss.Serialize(dict);
Console.WriteLine(json);
Be sure to add a reference to System.Web.Extensions.dll.
Note:
I usually deserialize to dynamic
, so I'm assuming that NameValueCollection
would work. However, I haven't verified if it actually does.
Unserialize JSON string
not exactly what I wanted but I will solve it like this
string jsonString = "{\r\n \"value1\":\"value2\" \r\n}";
string normalString = "value4";
var parsedJson = JObject.Parse(jsonString);
string valuefromJsonString = parsedJson["value1"].ToString();
NameValueCollection collection = new NameValueCollection();
collection["First"] = valuefromJsonString;
collection["Second"] = normalString;
var nvcDictionary = collection.AllKeys.ToDictionary(k => k, k => collection[k]);
var result = JsonConvert.SerializeObject(nvcDictionary);
which produces
{\"First\":\"value2\",\"Second\":\"value4\"}
Convert a NameValueCollection to a dynamic object
A quick google search turned up this:
http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/
So you can do:
IDictionary<string, string> dict = new Dictionary<string, string> { { "Foo", "Bar" } };
dynamic dobj = dict.ToExpando();
dobj.Foo = "Baz";
Is that what you are looking for?
How to deserialize name-value collection pairs from a JSON string into an object?
You will need to use a custom JsonConverter
to do what you want. Below is a generic one which should do the job. The converter works by first loading the JSON into a JObject
, then converterting the items array into regular properties on the JObject
, and finally populating the target class from the JObject
.
public class ClassWithItemsConverter<T> : JsonConverter where T : class, new()
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
JProperty items = jo.Property("items");
if (items != null && items.Value.Type == JTokenType.Array)
{
foreach (JObject item in items.Value.Children<JObject>())
{
jo.Add((string)item["name"], item["value"]);
}
items.Remove();
}
T result = new T();
serializer.Populate(jo.CreateReader(), result);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can use the converter by adding a [JsonConverter]
attribute to your target class like this:
[JsonConverter(typeof(ClassWithItemsConverter<Payment>))]
public class Payment
{
...
}
Here is a working demo: https://dotnetfiddle.net/IuHT3O
Related Topics
Can Razor Class Library Pack Static Files (Js, CSS etc) Too
Disabling Screen Saver and Power Options in C#
Get Affected Rows on Executenonquery
Async/Await Keywords Not Available in .Net 4.0
How to Export a Gridview.Datasource to a Datatable or Dataset
Open Link in New Tab (Webbrowser Control)
Unlock Windows Programmatically
MVC 4 How Pass Data Correctly from Controller to View
Entity Framework - Stored Procedure Return Value
Can a Picturebox Show Animated Gif in Windows Application
Does Dispose Still Get Called When Exception Is Thrown Inside of a Using Statement
Wpf How to Access Control from Datatemplate
Edit Raw Pixel Data of Writeablebitmap
How to Detect Which .Net Runtime Is Being Used (Ms VS. Mono)