Problems with Json Serialize Dictionary<Enum, Int32> - c#

whenever i try to serialize the dictionary i get the exception:
System.ArgumentException: Type
'System.Collections.Generic.Dictionary`2[[Foo.DictionarySerializationTest+TestEnum, Foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'
is not supported for serialization/deserialization of a dictionary,
keys must be strings or object
My Testcase is:
public class DictionarySerializationTest
{
public enum TestEnum { A, B, C }
//tried with numbers, too: public enum TestEnum { A = 1, B = 2, C = 3 }
public void SerializationTest()
{
Dictionary<TestEnum, Int32> data = new Dictionary<TestEnum, Int32>();
data.Add(TestEnum.A, 1);
data.Add(TestEnum.B, 2);
data.Add(TestEnum.C, 3);
JavaScriptSerializer serializer = new JavaScriptSerializer();
String result = serializer.Serialize(data);
// Throws
}
public void SerializationToObjectTest()
{
Dictionary<object, Int32> data = new Dictionary<object, Int32>();
data.Add(Enum.ToObject(typeof(TestEnum), TestEnum.A), 1);
data.Add(Enum.ToObject(typeof(TestEnum), TestEnum.B), 2);
data.Add(Enum.ToObject(typeof(TestEnum), TestEnum.C), 3);
JavaScriptSerializer serializer = new JavaScriptSerializer();
String result = serializer.Serialize(data);
// Throws
}
public void SerializationStringTest()
{
Dictionary<String, Int32> data = new Dictionary<String, Int32>();
data.Add(TestEnum.A.ToString(), 1);
data.Add(TestEnum.B.ToString(), 2);
data.Add(TestEnum.C.ToString(), 3);
JavaScriptSerializer serializer = new JavaScriptSerializer();
String result = serializer.Serialize(data);
// Succeeds
}
}
Of course i could use .ToString() whenever i enter something into the Dictionary but since it's used quite often in performance relevant methods i would prefer using the enum.
My only solution is using .ToString() and converting before entering the performance critical regions but that is clumsy and i would have to change my code structure just to be able to serialize the data.
Does anyone have an idea how i could serialize the dictionary as <Enum, Int32>?
I use the System.Web.Script.Serialization.JavaScriptSerializer for serialization.
UPDATE:
I switched to Dictionary<String, Int32> now and it works but i hope someone shows a solution as i don't really like using strings in place of a type safe enum.

I know it's late, but maybe someone else can make use of it in the future. You can achieve what you need using LINQ:
Dictionary<TestEnum, Int32> data = new Dictionary<TestEnum, Int32>();
data.Add(TestEnum.A, 1);
data.Add(TestEnum.B, 2);
data.Add(TestEnum.C, 3);
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, Int32> dataToSerialize = data.Keys.ToDictionary(p => p.ToString(), p => data[p]);
string dataSerialized = serializer.Serialize(dataToSerialize);

Use Newtonsoft (Newtonsoft.Json.dll) to serialize the Dictionary object and you'd be fine. It's a popular third party library you would have to download and include in your project as a reference.
See example below:
var _validationInfos = new Dictionary<ImportField, ValidationInfo>();
var serializedData = JsonConvert.SerializeObject(_validationInfos);

I think that you are having trouble because TestEnum is declared as a private enum. Try marking it as a public enum. The serializer needs to be able to find your enum via reflection in order to serialize it.
Also according to the Docs, the enums must have integer values. So you might want to write:
public enum TestEnum { A = 1, B = 2, C =3 }
Also, the docs say that this enum will just get mapped to its corresponding integer value during the serialization. So depending on what you are doing on the other end, Strings might be more expressive and easier to work with.

Exception says "keys must be strings or object" so try
data.Add(Enum.ToObject(typeof(TestEnum), TestEnum.A));
data.Add(Enum.ToObject(typeof(TestEnum), TestEnum.B));
data.Add(Enum.ToObject(typeof(TestEnum), TestEnum.C));
I did not test it though, just a guess.

I’ve created JavaScriptSerializer extension DeserializeDictionary- see http://geekswithblogs.net/mnf/archive/2011/06/03/javascriptserializer-extension-deserializedictionarytkey-tvalue.aspx
public static Dictionary<TKey, TValue> DeserializeDictionary<TKey, TValue>(this JavaScriptSerializer jss, string jsonText)
{
var dictWithStringKey = jss.Deserialize<Dictionary<string,TValue>>(jsonText);
var dict=dictWithStringKey.ToDictionary(de => jss.ConvertToType<TKey>(de.Key),de => de.Value);
return dict;
}

Related

C# how to cast object to a type returned from GetType()

// Assuming this object is allocated somewhere else
var dict = new Dictionary<int, string>
{
{1, "One"},
{2, "Two"}
};
// allocate a handle for it so it won't get destroyed by GC
var handle1 = GCHandle.Alloc(dict);
// Get internal representation of this object
IntPtr ptrObj = GCHandle.ToIntPtr(handle1);
// Assuming now we are in another module
var handle2 = GCHandle.FromIntPtr(ptrObj); // Get handle from IntPtr of this object
var objDict = handle2.Target; // Get object
// How to convert it back to Dictionary?
// When debug, I know its type is Dictionary<int, string>,
// but how I can know here?
// This conversion is not working
var myDict = (Dictionary<object, object>)objDict;
// Anyway to cast it from GetType()?
var myDict = (objDict.GetType())objDict;
In one module, I created a dictionary and convert to IntPtr (for PInvoke call).
In another module, I get the dictionary object from IntPtr, which is a generic object. How can I convert back to it original type so that I can use it like this:
var myDict = (objDict.GetType())objDict; // Assuming this works
var count = myDict.Count; // Get element count
The solution here seems to be to cast as type IDictionary. The Dictionary<TKey, TValue> class implements that interface so it will provide the basic, common functionality regardless of the type of the keys and values.
var handle2 = GCHandle.FromIntPtr(ptrObj);
var myDict = (IDictionary)handle2.Target;
You can then, for instance, pass a key as an object and get a value back as an object:
var value = myDict[key];

Why IsoDateTimeConverter is not used to serialize Dictionary<DateTime, int>

The result of a webAPI method that return a Dictionary is not serialized with the format defined in IsoDateTimeConverter
This is my configuration:
public static class WebApiConfig
{
//Web API configuration and services
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter
{
DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff'Z'"
});
}
}
Here a sample of webAPI method
[Route("GetPlanning")]
[HttpPost]
public Dictionary<DateTime, IEnumerable<int>> GetPlanning()
{
Dictionary<DateTime, IEnumerable<int>> planning = new Dictionary<DateTime,IEnumerable<int>>();
planning.Add(DateTime.UtcNow, new List<int>(){0,1,2});
return planning;
}
In client side the result is an object with a property that is a date without the millisecond.
If i return a array : return planning.ToArray(); the result is an array of object key value with a date with millisecond.
So why the format is apllied on a date in a array and not in a dictionary?
The reason IsoDateTimeConverter is not used for dictionary keys is that Json.NET does not serialize the keys - it merely converts them to strings. From the docs:
When serializing a dictionary, the keys of the dictionary are converted to strings and used as the JSON object property names. The string written for a key can be customized by either overriding ToString() for the key type or by implementing a TypeConverter. A TypeConverter will also support converting a custom string back again when deserializing a dictionary.
Thus converters, including IsoDateTimeConverter, are not used when converting dictionary keys to JSON.
That being said, IsoDateTimeConverter is no longer necessary. From Serializing Dates in JSON
From Json.NET 4.5 and onward dates are written using the ISO 8601 format by default, and using this converter is unnecessary.
The same date formatting can be obtained by setting JsonSerializerSettings.DateFormatString:
config.Formatters.JsonFormatter.SerializerSettings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff'Z'";
But how does this affect dictionary key conversion to JSON? As it turns out, Json.NET does respect this setting when converting a DateTime to a string as a dictionary key, as shown in the reference source. Thus:
var time = DateTime.Now;
Dictionary<DateTime, IEnumerable<int>> planning = new Dictionary<DateTime, IEnumerable<int>>();
planning.Add(DateTime.UtcNow, new List<int>() { 0, 1, 2 });
var root = new { today = time, planning = planning };
var settings = new JsonSerializerSettings
{
DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc,
DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff'Z'",
};
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
Console.WriteLine(json);
Produces the required output with consistent formatting for a DateTime value used as a dictionary keys and used as a property:
{
"today": "2016-09-09T03:54:51.704Z",
"planning": {
"2016-09-09T03:54:51.704Z": [
0,
1,
2
]
}
}

Converting dynamic type to dictionary C#

I have a dynamic object that looks like this,
{
"2" : "foo",
"5" : "bar",
"8" : "foobar"
}
How can I convert this to a dictionary?
You can fill the dictionary using reflection:
public Dictionary<String, Object> Dyn2Dict(dynamic dynObj)
{
var dictionary = new Dictionary<string, object>();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(dynObj))
{
object obj = propertyDescriptor.GetValue(dynObj);
dictionary.Add(propertyDescriptor.Name, obj);
}
return dictionary;
}
You can use a RouteValueDictionary to convert a C# object to a dictionary. See: RouteValueDictionary Class - MSDN. It converts object properties to key-value pairs.
Use it like this:
var toBeConverted = new {
foo = 2,
bar = 5,
foobar = 8
};
var result = new RouteValueDictionary(toBeConverted);
If the dynamic value in question was created via deserialization from Json.Net as you mentioned in your comments, then it should be a JObject. It turns out that JObject already implements IDictionary<string, JToken>, so you can use it as a dictionary without any conversion, as shown below:
string json =
#"{ ""blah"" : { ""2"" : ""foo"", ""5"" : ""bar"", ""8"" : ""foobar"" } }";
var dict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
dynamic dyn = dict["blah"];
Console.WriteLine(dyn.GetType().FullName); // Newtonsoft.Json.Linq.JObject
Console.WriteLine(dyn["2"].ToString()); // foo
If you would rather have a Dictionary<string, string> instead, you can convert it like this:
Dictionary<string, string> newDict =
((IEnumerable<KeyValuePair<string, JToken>>)dyn)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString());
You can use Json.Net to deserialize it to dictionary.
string json = dynamicObject.ToString(); // suppose `dynamicObject` is your input
Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
Very similar to ema answer, but with a one-liner using LINQ magic:
Dictionary<string, object> myDict = sourceObject.GetType().GetProperties().ToDictionary(prop => prop.Name, prop => prop.GetValue(sourceObject, null));
Another way is using System.Web.Helpers.Json included in .NET 4.5.
Json.Encode(object) and Json.Decode. Like:
Json.Decode<Generic.Dictionary<string, string>>(value);
MSDN: https://msdn.microsoft.com/en-us/library/gg547931(v=vs.111).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
Regards,
MarianoC.
You can do it with jsonSerializer. And it requires System.Net.Extensions reference. Here is a sample code.
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<Dictionary<string,string>>(jsonText);
var place = dict["8"]; // "foobar"
If you use the dynamic implementation here:
https://github.com/b9chris/GracefulDynamicDictionary
You can get the Dictionary right from the implementation. One advantage to using the above implementation (written for an answer to another SO question), is you can shift easily between the specific implementation and dynamic, like so:
dynamic headers = new DDict();
headers.Authorization = token;
if (doesNeedSiteId)
headers.SiteId = siteId;
await post(headers);
}
protected async Task post(DDict headers)
{
var dict = headers.GetDictionary(); // Dictionary<string, object>
In the above, the headers collection is conveniently created as a dynamic, but, the underlying specific implementation is DDict, and the post() method accepts it even though you've declared it as dynamic and used its features.

Servicestack serialization

For example I have this object
JsonObject o = new JsonObject();
o.Add("k1","0123");
o.Add("k2","123.");
When I serialize this json object the result is wrong:
{
"k1":0123,"k2":123.
}
It assumes that, those are numbers. Whereas these are incorrect numbers.
We have problems deserializing them on client side.
The correct value should be like below:
{
"k1":"0123","k2":"123."
}
How can I achieve this?
How to disable detecting numbers at all???
Below configurations did not help, I think they are for other purposes
JsConfig.TryToParseNumericType = false;
JsConfig.TryToParsePrimitiveTypeValues = false;
JsonObject class derives from Dictionary<string, string> so I don't think there is much you can do with that. You can create an instance of Dictionary<string, object> instead.
[Test]
public void SerializerTest()
{
Dictionary<string, object> o = new Dictionary<string, object>();
o.Add("k1", "0123");
o.Add("k2", "123.");
Assert.AreEqual("{\"k1\":\"0123\",\"k2\":\"123.\"}",
JsonSerializer.SerializeToString(o));
}
I would recommend you use the JsonConvert class, which handles JSON really well.
Usage would be as follows:
var serialized = JsonConvert.SerializeObject(new { k1 = "0123", k2 = "123" });
dynamic deserialized = JsonConvert.DeserializeObject(serialized);
Assert.That(deserialized.k1.ToString(), Is.EqualTo("0123")); // deserialized.k1 will be an object of 'JValue', so need to call toString()

set property type dynamically in c#

I got the following problem with my data stream:
The data stream consists of a dictionary, which I want to parse and specify the type of the value dynamically.
I.e. my data strea includes:
"date", "01.01.2000"
"name", "joe"
"alive", "true"
"health", "100"
Now I would like to set my properties of a generic class according to this data stream:
class GenericClass
{
Hashtable genericAttributes;
}
Is there a possibility to set my values of the data stream to the correct type via reflection?
I could try something like:
DateTime.TryParse(date, out myDate);
for the date time object, but I don't think this will work when trying to parse doubles, floats, int16s, int32s, uint16,...
Some thoughts on that?
Thx and regards
My guess from your question is that they all are IConvertible, so I would do something my code example below. The idea is that I specify the "want"-order, ie in the order I want the types if they can fit multiple types, and then I try to convert them in that order.
public class GenericPropClass
{
public Type type;
public object value;
public string key;
}
[TestMethod]
public void PropertySet()
{
var dict = new Dictionary<string, string>();
var resultingList = new List<GenericPropClass>();
// Specify the order with most "specific"/"wanted" type first and string last
var order = new Type[] { typeof(DateTime), typeof(int), typeof(double), typeof(string) };
foreach (var key in dict.Keys)
foreach (var t in order)
{
try
{
var res = new GenericPropClass()
{
value = Convert.ChangeType(dict[key], t),
key = key,
type = t,
};
resultingList.Add(res);
break;
}
catch (Exception)
{
// Just continue
}
}
}
Sorry for a short answer containing almost only code, I might have time to improve it tonight to get my thoughts in, but I have to go now :)

Categories