I'm trying to serialize a Dictionary with protobuf-net but still getting "Unexpected sub-type: " exception with generic lists.
This is how I construct the dictionary:
var list = new List<DummyObject1>();
list.Add(new DummyObject1());
var dict = new Dictionary<string, object>();
dict["0"] = new DummyObject1();
dict["1"] = new DummyObject1();
dict["2"] = list;
I added the types to the default model with
model.Add(typeof(object), true);
model.Add(typeof (DummyObject1), true);
Is there any solution or workaround for this situation?
Edit: I made it work with a workaround like this;
First I wrote a wrapper class for List
public class ListWrapper
{
public List<object> Items { get; set; }
}
and I registered the type and Items field
ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(ListWrapper));
ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(ListWrapper)].AddField(1, "Items");
Now I can serialize and deserialize a dictionary
var dict = new Dictionary<string, object>();
dict["0"] = new DummyObject1();
var lw = new ListWrapper { Items = new List<object> { new DummyObject1() } };
dict["2"] = lw;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, dict);
stream.Seek(0, SeekOrigin.Begin);
var req = Serializer.Deserialize<Dictionary<string, object>>(stream);
}
Quite simply, protobuf-net does not play nicely with object, and will always struggle with (for example) a Dictionary<string,object>. That isn't a supported use case, as the google protobuf specification expects data to follow a single contract end-to-end. I can spoof around that for some inheritance scenarios, but object is just too vague.
Related
I have a worker with various fields that are fetched from server. I am using CSVHelper package to convert this class to an excel sheet.
Worker has Fields like :
class Worker
{
string name;
string phone;
string age;
Dictionary<string,object> customerField;
}
I can map the name, phone, number like
class WorkerMap : CsvClassMap<Worker>
{
public WorkerMap()
{
Map(m => m.name);
Map(m => m.phone);
Map(m => m.age);
}
}
And I generate the map by :
csv.Configuration.RegisterClassMap<WorkerMap>();
Write the list of workers by :
csv.WriteRecords(workerList);
How can I map the customerField dictionary to the excel sheet such that the Key (string) is another column name and the value(object) is the value of the column.
Does CSVHelper help us do it at runtime. I looked through the documentation. Couldn't find anything that worked for me.
I don't think that writing a dictionary is supported at this time. For one thing, CsvHelper would have a difficult time knowing what headers to write. Fortunately, it's not too complex to use CsvWriter manually, writing a field at a time. If we assume that each Worker has the same keys in customerField then your code might look something like this.
var firstWorker = workerList.First();
var keys = firstWorker.customerField.Keys.ToList();
var headers = new []{ "name", "phone", "age"}.Concat(keys).ToList();
var csv = new CsvWriter( textWriter );
// Write the headers
foreach( var header in headers )
{
csv.WriteField(header);
}
csv.NextRecord();
// Write the rows
foreach( var item in workerList)
{
csv.WriteField(item.name);
csv.WriteField(item.phone);
csv.WriteField(item.age);
var dict = worker.customerField;
foreach (var key in keys)
{
csv.WriteField(dict[key]);
}
csv.NextRecord();
}
This code is untested, but should get you pretty close to the behavior you need. If the customerField dictionary keys are not consistent in the list then the code would be a bit more complicated but it's still solvable.
Dictionary isn't supported but ExpandoObject is supported.
https://github.com/JoshClose/CsvHelper/blob/48e70742e06007dae3a635c418b7e3358f667c4f/src/CsvHelper.Tests/Writing/MultipleHeadersTest.cs
https://github.com/JoshClose/CsvHelper/blob/b74a2f95a101158f4cdedd25fae6e8392b58855b/src/CsvHelper.Tests/Writing/DynamicTests.cs
If you follow the first link above you'll find the WriteDynamicHeader method in use on lines 50 & 57.
With help of an extension method I create an ExpandoObject for each record and use CsvHelper to write that object.The Dictionary<string, object> parameter named document is what I wish to create the CSV record from.
public static class DictionaryCsvExtentions
{
public static dynamic BuildCsvObject(this Dictionary<string, object> document)
{
dynamic csvObj = new ExpandoObject();
foreach (var p in document)
{
AddProperty(csvObj, p.Key, p.Value);
}
return csvObj;
}
private static void AddProperty(ExpandoObject expando, string propertyName, object propertyValue)
{
var expandoDict = expando as IDictionary<string, object>;
if (expandoDict.ContainsKey(propertyName))
{
expandoDict[propertyName] = propertyValue;
}
else
{
expandoDict.Add(propertyName, propertyValue);
}
}
}
Now I can create an ExpandoObject from my dictionary like this
var csvObj = myDictonary.BuildCsvObject();
and with that, following Josh's tests in the link above we have all we need to use a dictionary fairly seamlessly with CsvHelper. I don't think this is a better solution to Michael's, just a different approach.
credit where credit is due the basic ExpandoObject from dictionary code is from here (where there is a lot more explanation!) https://www.oreilly.com/learning/building-c-objects-dynamically
Recently had the same problem and for the sake of completeness posting another answer. We had fairly complex ClassMap<Worker> like class and did want to loose it. Also, we needed both CSV write and read hence the ExpandoObject from the robs answer was needed as well. Eventually, the approach is like combination of the Michael Richardson and robs answer that should bring best of both worlds.
On top of that, to distinguish dictionary fields in the CSV file during read it is a good idea to prefix them with something like "customerField.".
First we need conversion of dictionary to/from Worker.customerField:
public static class WorkerExtensions
{
const string CustomerFieldPrefix = nameof(Worker.customerField) + ".";
public static dynamic GetCustomerFieldExpando(this Worker worker)
{
var expando = new ExpandoObject() as IDictionary<string, object>;
foreach (var fieldPair in worker.customerField)
{
expando[CustomerFieldPrefix + fieldPair.Key] = fieldPair.Value ?? "";
}
return expando;
}
public static void SetCustomerField(this Worker worker, ExpandoObject expando)
{
var columnsToValues = expando as IDictionary<string, object>;
foreach (var columnValuePair in columnsToValues)
{
if (columnValuePair.Key.StartsWith(CustomerFieldPrefix)
&& columnValuePair.Key.Length > CustomerFieldPrefix.Length)
{
string key = columnValuePair.Key.Substring(CustomerFieldPrefix.Length);
worker.customerField[key] = columnValuePair.Value;
}
}
}
}
Next, CSV write can use both ClassMap and ExpandoObject and looks like this:
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.RegisterClassMap<WorkerMap>();
csv.WriteHeader<Worker>();
(workers.First().GetCustomerFieldExpando() as IDictionary<string, object>)
.Keys.ToList().ForEach(key => csv.WriteField(key));
csv.NextRecord();
foreach (var worker in workers)
{
csv.WriteRecord(worker);
csv.WriteRecord(worker.GetCustomerFieldExpando());
csv.NextRecord();
}
Finally, CSV read can also combine both ClassMap and ExpandoObject:
List<Worker> workers = new List<Worker>();
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.RegisterClassMap<WorkerMap>();
csv.Read();
csv.ReadHeader();
var columns = csv.Context.HeaderRecord;
while (csv.Read())
{
var worker = csv.GetRecord<Worker>();
workers.Add(worker);
ExpandoObject expando = csv.GetRecord<dynamic>();
worker.SetCustomerField(expando);
}
In case of CSV read things get more complicated if you want to read real types into dictionary values (not just strings). We needed predefined associations between dictionary keys and data types to be able to convert to proper types from the ExpandoObject.
I have a generic list of type object and I am trying to serialize it , but deserializing doesn't fetch me good results.
Here is what I am trying to do:
List<object> sample_list = new List<object>();
sample_list.Add(new Sample() { type=0,message="I am the first"});
sample_list.Add(new Sample1() { type=1,message1 = "I am the 2" });
sample_list.Add(new Sample2() { type=2,message2 = "I am the 3" });
string serial= JsonConvert.SerializeObject(sample_list);
List<object> list = JsonConvert.DeserializeObject<List<object>>(serial);
lstbox.ItemsSource = list;
foreach(var item in list)
{
if (item is Sample)
{
MessageBox.Show("Item is sample");
}
}
But the Message box never shows.
What should be done so that it can work correct?
You are deserializing list into list of object, why you expect then CLR will recognize those objects as Sample or Sample1? Serialized JSON looks:
[{"type":0,"message":"I am the first"},{"type":1,"message1":"I am the 2"},{"type":2,"message2":"I am the 3"}]
So how JSON.NET could magically find out that they are Sample objects? Make a simple test to notice that list[0].GetType().FullName is Newtonsoft.Json.Linq.JObject, not Sample.
If you want to deserialize to list of Sample, write:
var list = JsonConvert.DeserializeObject<List<Sample>>(serial);
and then Json.NET will try to match each property from JSON into Sample properties (but of course it won't success sometimes as not objects are of type Sample).
If you want to serialize your list, your JSON should store information about used types, and there is built-in option for it in JSON.NET:
string serial = JsonConvert.SerializeObject(sample_list,
new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Objects
});
Then serialized JSON looks:
[{"$type":"ConsolePusher.Sample, ConsolePusher","type":0,"message":"I am the first"},
{"$type":"ConsolePusher.Sample1, ConsolePusher","type":1,"message1":"I am the 2"},
{"$type":"ConsolePusher.Sample2, ConsolePusher","type":2,"message2":"I am the 3"}]
So it will be able to recreate objects when deserializing:
var list = JsonConvert.DeserializeObject<List<object>>(serial,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
The clue here should be the json:
[{"type":0,"message":"I am the first"},
{"type":1,"message1":"I am the 2"},
{"type":2,"message2":"I am the 3"}]
You are deserializing that as List<object>. So: there is nothing in either the json or the API call that gives it a hint that you want a Sample. If you want it to store the type name and use that during deserialization, you need to enable that option:
var settings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Objects
};
string serial = JsonConvert.SerializeObject(sample_list, settings);
List<object> list =
JsonConvert.DeserializeObject<List<object>>(serial, settings);
Note that storing type names is brittle and implementation-specific; it will not work well in all scenarios. The json here becomes:
[{"$type":"Sample, ConsoleApplication43","type":0,"message":"I am the first"},
{"$type":"Sample1, ConsoleApplication43","type":1,"message1":"I am the 2"},
{"$type":"Sample2, ConsoleApplication43","type":2,"message2":"I am the 3"}]
What is the proper way to deserialize this JSON string? It is simply an array of dictionaries where each dict has a "title" and "children" where children is another array of dicts.
I am using this as a TreeView item source, but the treeview only displays the Title1 > Child1 because I assume something is wrong with the deserializing I'm doing. I also try to print out Child1's first child but can't figure out how to do it. The code below has an invalid cast exception.
s = #"[{""title"":""Title1"",""children"":[{""title"":""Child1"",""children"":[{""title"":""grandchild1"",""children"":[{""title"":""Huh""}]}] }] }]";
List<Dictionary<string, object>> marr = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(s);
mTreeView.ItemsSource = marr;
List<Dictionary<string,object>> cs = (List<Dictionary<string,object>>)marr[0]["children"];
Debug.WriteLine(cs[0]["title"]);
Looks to me like you have the following:
class MyObject
{
public string title { get; set; }
public List<MyObject> children { get; set; }
}
var deserialized = JsonConvert.DeserializeObject<List<MyObject>>(s);
And no, there's no dictionary here, because:
Try as I might, I don't see a "dictionary" here so much as a recursive list of the object above, and
This is the actual definition of the object your want out anyway, so you can take advantage of all the benefits of having a real type, rather than just a dictionary of strings.
Note to address your comments: If the JSON string changes, it will not break your code; extraneous properties will be ignored and missing properties will correctly get set to null.
https://codetitans.codeplex.com/
codetitans JSON supports correct parsing of JSON into array/dict of primitives as follows:
JSonReader jr = new JSonReader();
IJSonObject json = jr.ReadAsJSonObject(s);
Debug.WriteLine(json[0]["children"][0]["title"]);
As far as I can tell it is the only C# library that does.
It looks like you can do this with JSON.NET out of box currently
var #object = JsonConvert.DeserializeObject(s)
var slightlyMoreUsefulObject = (JArray)#object;
var actualObject = slightlyMoreUsefulObject[0]
var topLevelTitle = actualObject["title"]
var children = actualObject["children"]
var firstChild = children[0]
var firstChildTitle = firstChild["title"]
In c# language using dictionary to store and that dictionary converted to object at finally that object stored to SQL server..
You could serialize it and save it as a json string. You could use http://nuget.org/packages/newtonsoft.json to serialize and deserialize you dictionary.
If you really want to convert the dictionary to an object that has the items of the dictionary as properties, you can use ExpandoObject:
var dict = new Dictionary<string, object> { { "Property", "foo" } };
var eo = new ExpandoObject();
var eoColl = (ICollection<KeyValuePair<string, object>>)eo;
foreach (var kvp in dict)
{
eoColl.Add(kvp);
}
dynamic eoDynamic = eo;
string value = eoDynamic.Property;
But I'm not sure how is doing that going to help you
The .NET Dictionary<TKey, TValue> object allows assignment of key/values like so:
Dictionary<string, string> dict = new Dictionary<string, string>();
dict["1"] = "foo";
dict["2"] = "bar";
but I cannot use a Dictionary like so:
Dictionary<string, string> dict = new Dictionary<string, string>();
dict["F1"]["F2"]["F3"] = "foo";
dict["2"]["X"] = "bar";
Is there a collection in .NET which allows me to nest [], or would I have to create my own?
If I have to create my own, how would I do this?
EDIT:
It would also be useful if I could have implementations which expect unique keys, like so:
dict["F1"]["F2"]["F3"] = "foo";
dict["F1"]["F2"]["F3"] = "bar"; //result is "bar" because "foo" was overridden
and an implementation where a key can be used more than once
dict["F1"]["F2"]["F3"] = "foo";
dict["F1"]["F2"]["F3"] = "bar"; //result can be "foo" and "bar"
Is this possible?
EDIT (as per Jon Skeet's question):
I want to use the structure like so (as a very rough example):
json["data"]["request"]["name"] = "username";
json["data"]["request"]["pass"] = "password";
resolves to
{ data: { request: { name: "username", pass: "password" } } }
and equally there would be an equivalent for XML etc.
Having a requirement for arbitrarily long nesting, I have come up with the following solution, which as far as I can see, doesn't break, according to my test:
public class NestedDictionary<K, V> : Dictionary<K, NestedDictionary<K, V>>
{
public V Value { set; get; }
public new NestedDictionary<K, V> this[K key]
{
set { base[key] = value; }
get
{
if (!base.Keys.Contains<K>(key))
{
base[key] = new NestedDictionary<K, V>();
}
return base[key];
}
}
}
TEST:
NestedDictionary<string, string> dict = new NestedDictionary<string, string>();
dict["one"].Value = "Nest level 1";
dict["one"]["two"]["three"].Value = "Nest level 3";
dict["FieldA"]["FieldB"].Value = "Hello World";
Console.WriteLine(dict["one"].Value);
Console.WriteLine(dict["one"]["two"]["three"].Value);
Console.WriteLine(dict["FieldA"]["FieldB"].Value);
You can do this using the standard Dictionary, you just have to declare the nesting:
Dictionary<string, Dictionary<string, string>> dict = ...
string test = dict["first"]["second"]
Dictionary<string, Dictionary<string, Dictionary<string, string>>> dict = ...
string test = dict["first"]["second"]["third"]
etc
The original Dictionary COM object which was created to work with vb6 would respond to an attempt to access a non-existent item by creating a new item of type Dictionary with the corresponding name. This approach allows something to be stored to MyDict["Foo"]["Bar"] without having to first create MyDict["Foo"]. The problem with this approach is that while one would want to add "Foo" to MyDict when performing a write to MyDict["Foo"]["Bar"], one would rather not create such an item if one was attempting to e.g. evaluate MyDict["Foo"]["Bar"].ValueOrDefault(someDefaultValue).
I've used such collections, since they can be handy for modeling certain things (conceptually they're a lot like XML documents). One workable approach is to declare that dictionaries which contain nothing but other dictionaries are considered semantically as non-entities which may be removed at any opportunity. When implicitly adding a subcollection, set a flag in the item to which it's added it indicating that it should be checked for items that may be deleted (or keep a counter of how many such items may exist). Then with some reasonable frequency, scan through the dictionaries and remove such "dead" items.
An alternative approach is to have the indexer from the dictionary not return an actual item, but instead return an "ephemeral indexer" type, which keeps a reference to the parent object and has internal methods GetNestedForReading, SetNestedForReading, GetValue, and SetValue, which chain back to it. Then a statement Foo["Bar"]["Boz"] = "George"; will end up effectively performing Foo.SetNestedForReading("Bar").SetValue("Boz", "George"); while z = Foo["Bar"]["Boz"]; will effectively perform Foo.GetNestedForReading("Bar").GetValue("Boz");. Calling SetNestedForReading method with a non-existent key will create and return a new nested item; the GetNestedForReading method will an immutable "empty" item. Using this approach will thus avoid creating empty items.
Although the latter approach is more complicated than the former, it has another advantage. It's possible to have each node to individually hold its collection as either a shared deeply-immutable dictionary or an unshared mutable one; if a GetNestedForWriting call sees that the nested object is immutable, it can construct a new shallowly-mutable object holding the same items. If one defines the cloning method for a mutable node as creating a new immutable node with (immutable) clones of all subnodes, and the cloning method of an immutable node as returning itself, cloning trees that are mostly immutable becomes very cheap. If one had a newly-cloned (thus immutable) four-level tree with sixteen items on each level (65,536 leaf nodes total) and all the nodes were shared-immutable, updating a leaf node would only require replacing one leaf and four other nodes with mutable ones. Cloning the tree again would only require creating new immutable objects for the nodes which had been replaced with mutable ones (e.g. copying five things). Although one would have the convenience of a fully-mutable tree, one would have the efficiency advantages of an immutable one.
The biggest "problem" I see with this approach is that to avoid some weird behaviors one must require the use of syntax like MyDict["Foo"]["Bar"].Value = "George". If implicit conversion operators were used to avoid that requirement, someone would expect a statement like var st = MyThing["Foo"]["Bar"]; to define st as a string snapshot of whatever MyThing["Foo"]["Bar"] holds at that moment; instead it would define it as something that will index MyThing["Foo"]["Bar"]. If one had to use .Value to read or write strings from such a type, the fact that the variable wasn't a string would be apparent. If one used implicit operators to allow such assignments, the behavior would be odd. It's too bad there's no way a function can specify "do not allow this return value to be used for type inference".
Incidentally, it's possible to have the indexer type be a class or a generic struct. If it's a class, an access to foo["Bar"]["boz"]["baz"]... nested N deep would likely require the creation of N temporary heap objects. If it's a generic struct, it would entail the creation of N structs, but the more-deeply-nested structs would get bigger. For reasonable levels of nesting, generic structs would probably be slightly more efficient, but classes would probably be easier to work with.
You have to decide on either supporting a fixed number of string keys to look up, or provide a more general key mechanism if the number of keys can vary. For the first case try the following:
Dictionary<string,Dictionary<string,string>> dict =
Dictionary<string,Dictionary<string,string>>();
dict["F1"]["F2"] = "foo";
Dictionary<string,Dictionary<string,Dictionary<string,string>>> dict2 =
Dictionary<string,Dictionary<string,string>>();
dict2["F1"]["F2"]["F3"] = "bar";
For the second case, you could do the following:
Dictionary<string[],string> dict = new Dictionary<string[],string>(new MyEqualityComparer());
dict[new string[] {"F1","F2"}] = "foo";
dict[new string[] {"F1","F2","F3"}] = "bar";
where the class MyEqualityComparer would be something like:
public class MyEqualityComparer : IEqualityComparer<string[]>
{
public int GetHashCode(string[]item)
{
int hashcode = 0;
foreach (string s in item)
{
hashcode |= s.GetHashCode();
}
return hashcode;
}
public bool Equals(string [] a, string [] b)
{
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length; ++i)
{
if (a[i] != b[i])
return false;
}
return true;
}
Use a Dictionary as TValue:
var dict2 = new Dictionary<string, Dictionary<string, string>>();
var dict3 = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
for example:
var dict =
new Dictionary<string, Dictionary<string, string>>
{
{
"F1", new Dictionary<string, string>
{
{"F2", "foo"}
}
}
};
dict["F1"]["F2"] = "bar";
I think, your case is good place to use DynamicObject. I'll create an example for json using Dictionary<string, object> internally.
Same idea can be used for xml also.
string json = #"{""Name"":""Joe"",
""Age"":30,
""Address"":{ ""City"":""NY"" }}";
dynamic dynObj = new DynamicJson(json);
Console.WriteLine(dynObj.Name);
Console.WriteLine(dynObj.Age);
Console.WriteLine(dynObj.Address.City);
--
public class DynamicJson : DynamicObject
{
Dictionary<string, object> _Dict;
public DynamicJson(string json)
{
_Dict = (Dictionary<string, object>)new JavaScriptSerializer().DeserializeObject(json);
}
DynamicJson(Dictionary<string, object> dict)
{
_Dict = dict;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
object obj;
if (!_Dict.TryGetValue(binder.Name, out obj)) return false;
if (obj is Dictionary<string, object>)
{
result = new DynamicJson((Dictionary<string, object>)obj);
}else
{
result = obj;
}
return true;
}
}