Write Dictionary which has Objects to file - c#

I have such dictionary Dictionary<string, object>, dictionary holds string keys and objects as values. I need to save and later load such dictionary. What would be best method to do that?

You can use this serializable Dictionary<TKey, TVal>(tested):
http://www.dacris.com/blog/2010/07/31/c-serializable-dictionary-a-working-example/
Dictionary<String, Object> otherDictionary = new Dictionary<String, Object>();
otherDictionary.Add("Foo", new List<String>() { "1st Foo","2nd Foo","3rd Foo" });
var dict = new SerializableDictionary<String, Object>(otherDictionary);
write it to a file:
using (FileStream fileStream = new FileStream("test.binary", FileMode.Create))
{
IFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(fileStream, dict);
}
read it from a file:
using (FileStream fileStream = new FileStream("test.binary", FileMode.Open))
{
IFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
dict = (SerializableDictionary<String, Object>)bf.Deserialize(fileStream);
}
Note: Of course you don't need to create a second Dictionary. You can use the SerializableDictionary at the first place. That should just demonstrate how to use it with an already existing Dictionary.

You can use NewtonSoft Json.net: http://james.newtonking.com/projects/json-net.aspx.
It is flexible and very friendly.

How about serializing it to XML?
http://support.microsoft.com/kb/815813
object yourType = new Object();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(yourType.GetType());
x.Serialize(Console.Out,yourType);

If you don't mind adding in an extra dependency, you can use protobuf.net. This has the benefit of being fast (definitely faster than the XML serializer approach), and it works on the bog standard Dictionary.
Reference it in your assembly, then the following shows how to use it with a MemoryStream:
class Program
{
static void Main(string[] args)
{
Dictionary<int, string> dict = new Dictionary<int, string>();
for (int i = 0; i < 10; i++)
{
dict[i] = i.ToString();
}
using (var ms = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(ms, dict);
ms.Seek(0, SeekOrigin.Begin);
var dict2 = ProtoBuf.Serializer.Deserialize<Dictionary<int, string>>(ms);
}
}
}
So long as both the key and value type for the dictionary are serializable by protobuf.net, this will work.
EDIT:
If you want to use this approach, you have to make your key and value object's class serializable by ProtoBuf. In short, attach [ProtoContract] to the class, and attach e.g. [ProtoMember(1)] to each Property you want to be serialized. See the website for more details. Note both string and int are serializable out of the box.

Related

Serialising a collection is converting to a JObject rather than a JArray [duplicate]

I am simply trying to serialize and deserialize a string array in Bson format using Json.NET, but the following code fails:
var jsonSerializer = new JsonSerializer();
var array = new string [] { "A", "B" };
// Serialization
byte[] bytes;
using (var ms = new MemoryStream())
using (var bson = new BsonWriter(ms))
{
jsonSerializer.Serialize(bson, array, typeof(string[]));
bytes = ms.ToArray();
}
// Deserialization
using (var ms = new MemoryStream(bytes))
using (var bson = new BsonReader(ms))
{
// Exception here
array = jsonSerializer.Deserialize<string[]>(bson);
}
Exception message:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.String[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
How can I get this to work?
Set ReadRootValueAsArray to true on BsonReader
http://james.newtonking.com/projects/json/help/index.html?topic=html/P_Newtonsoft_Json_Bson_BsonReader_ReadRootValueAsArray.htm
This setting is required because the BSON data spec doesn't save metadata about whether the root value is an object or an array.
Hmmm, from where I sit, your code should work, but Json.Net seems to think that your serialized array of strings is a dictionary. This could be because, according to the BSON specification, arrays actually do get serialized as a list of key-value pairs just like objects do. The keys in this case are simply the string representations of the array index values.
In any case, I was able to work around the issue in a couple of different ways:
Deserialize to a Dictionary and then manually convert it back to an array.
var jsonSerializer = new JsonSerializer();
var array = new string[] { "A", "B" };
// Serialization
byte[] bytes;
using (var ms = new MemoryStream())
using (var bson = new BsonWriter(ms))
{
jsonSerializer.Serialize(bson, array);
bytes = ms.ToArray();
}
// Deserialization
using (var ms = new MemoryStream(bytes))
using (var bson = new BsonReader(ms))
{
var dict = jsonSerializer.Deserialize<Dictionary<string, string>>(bson);
array = dict.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToArray();
}
Wrap the array in an outer object.
class Wrapper
{
public string[] Array { get; set; }
}
Then serialize and deserialize using the wrapper object.
var jsonSerializer = new JsonSerializer();
var obj = new Wrapper { Array = new string[] { "A", "B" } };
// Serialization
byte[] bytes;
using (var ms = new MemoryStream())
using (var bson = new BsonWriter(ms))
{
jsonSerializer.Serialize(bson, obj);
bytes = ms.ToArray();
}
// Deserialization
using (var ms = new MemoryStream(bytes))
using (var bson = new BsonReader(ms))
{
obj = jsonSerializer.Deserialize<Wrapper>(bson);
}
Hope this helps.
As explained in this answer by James Newton-King, the BSON format doesn't save metadata about whether the root value is a collection, making it necessary to set BsonDataReader.ReadRootValueAsArray appropriately before beginning to deserialize.
One easy way to do this, when deserializing to some known POCO type (rather than dynamic or JToken), is to initialize the reader based on whether the root type will be serialized using an array contract. The following extension methods do this:
public static partial class BsonExtensions
{
public static T DeserializeFromFile<T>(string path, JsonSerializerSettings settings = null)
{
using (var stream = new FileStream(path, FileMode.Open))
return Deserialize<T>(stream, settings);
}
public static T Deserialize<T>(byte [] data, JsonSerializerSettings settings = null)
{
using (var stream = new MemoryStream(data))
return Deserialize<T>(stream, settings);
}
public static T Deserialize<T>(byte [] data, int index, int count, JsonSerializerSettings settings = null)
{
using (var stream = new MemoryStream(data, index, count))
return Deserialize<T>(stream, settings);
}
public static T Deserialize<T>(Stream stream, JsonSerializerSettings settings = null)
{
// Use BsonReader in Json.NET 9 and earlier.
using (var reader = new BsonDataReader(stream) { CloseInput = false }) // Let caller dispose the stream
{
var serializer = JsonSerializer.CreateDefault(settings);
//https://www.newtonsoft.com/json/help/html/DeserializeFromBsonCollection.htm
if (serializer.ContractResolver.ResolveContract(typeof(T)) is JsonArrayContract)
reader.ReadRootValueAsArray = true;
return serializer.Deserialize<T>(reader);
}
}
}
Now you can simply do:
var newArray = BsonExtensions.Deserialize<string []>(bytes);
Notes:
BSON support was moved to its own package, Newtonsoft.Json.Bson, in Json.NET 10.0.1. In this version and later versions BsonDataReader replaces the now-obsolete BsonReader.
The same extension methods can be used to deserialize a dictionary, e.g.:
var newDictionary = BsonExtensions.Deserialize<SortedDictionary<int, string>>(bytes);
By checking the contract type ReadRootValueAsArray is set appropriately.
Demo fiddle here.
In general, you could check data type first before set ReadRootValueAsArray to true, like this:
if (typeof(IEnumerable).IsAssignableFrom(type))
bSonReader.ReadRootValueAsArray = true;
I know this is an old thread but I discovered a easy deserialization while using the power of MongoDB.Driver
You can use BsonDocument.parse(JSONString) to deserialize a JSON object so to deserialize a string array use this:
string Jsonarray = "[\"value1\", \"value2\", \"value3\"]";
BsonArray deserializedArray = BsonDocument.parse("{\"arr\":" + Jsonarray + "}")["arr"].asBsonArray;
deserializedArray can then be used as any array such as a foreach loop.

serialize/deserialize a list of objects using BinaryFormatter

I know there were already many discussions on that topic, like this one:
BinaryFormatter and Deserialization Complex objects
but this looks awfully complicated. What I'm looking for is an easier way to serialize and deserialize a generic List of objects into/from one file. This is what I've tried:
public void SaveFile(string fileName)
{
List<object> objects = new List<object>();
// Add all tree nodes
objects.Add(treeView.Nodes.Cast<TreeNode>().ToList());
// Add dictionary (Type: Dictionary<int, Tuple<List<string>, List<string>>>)
objects.Add(dictionary);
using(Stream file = File.Open(fileName, FileMode.Create))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(file, objects);
}
}
public void LoadFile(string fileName)
{
ClearAll();
using(Stream file = File.Open(fileName, FileMode.Open))
{
BinaryFormatter bf = new BinaryFormatter();
object obj = bf.Deserialize(file);
// Error: ArgumentNullException in System.Core.dll
TreeNode[] nodeList = (obj as IEnumerable<TreeNode>).ToArray();
treeView.Nodes.AddRange(nodeList);
dictionary = obj as Dictionary<int, Tuple<List<string>, List<string>>>;
}
}
The serialization works, but the deserialization fails with an ArgumentNullException. Does anyone know how to pull the dictionary and the tree nodes out and cast them back, may be with a different approach, but also nice and simple? Thanks!
You have serialized a list of objects where the first item is a list of nodes and the second a dictionary. So when deserializing, you will get the same objects back.
The result from deserializing will be a List<object>, where the first element is a List<TreeNode> and the second element a Dictionary<int, Tuple<List<string>, List<string>>>
Something like this:
public static void LoadFile(string fileName)
{
ClearAll();
using(Stream file = File.Open(fileName, FileMode.Open))
{
BinaryFormatter bf = new BinaryFormatter();
object obj = bf.Deserialize(file);
var objects = obj as List<object>;
//you may want to run some checks (objects is not null and contains 2 elements for example)
var nodes = objects[0] as List<TreeNode>;
var dictionary = objects[1] as Dictionary<int, Tuple<List<string>,List<string>>>;
//use nodes and dictionary
}
}
You can give it a try on this fiddle.

How to deep copy a Dictionary containing a List in C#?

I need to a make a deep copy of a Dictionary<string, List<int>>. I tried:
Dictionary<string, List<int>> tmpStudents =
new Dictionary<string, List<int>>(students);
but manipulating the values in tmpStudents is still changing those in students. It seems that the lists in tmpStudents are still referencing the lists in students, but I'm not sure how to remedy this without manually deep copying each list.
You need to deep-copy the lists as well; all you have done is copy the dictionary, but all of the list references are still shared between them.
This is fairly easy using LINQ:
var tmpStudents = students.ToDictionary(p => p.Key, p => p.Value.ToList());
Try this:
var tmpStudents = students.ToDictionary(x => x.Key, x => x.Value.ToList());
Because you have a List<int> and int is a value type this should work. Otherwise you have to create a deep copy for each value separately.
Serialize your Dictionary to a MemoryStream using a BinaryFormatter, and then deserialize it back to your variable:
using (MemoryStream ms = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, oldStudants);
var newStudants = (Dictionary<String, List<int>>)formatter.Deserialize(ms);
}

Deep Copy of OrderedDictionary

What is the simplest way to create a deep copy of an OrderedDictionary? I tried making a new variable like this:
var copy = dict[x] as OrderedDictionary;
But if I update the values/keys in copy, the dictionary in dict[x] gets updated as well.
Edit: dict is another OrderedDictionary.
You should be able to use a generic deep cloning method. An example of deep cloning from msdn magazine:
Object DeepClone(Object original)
{
// Construct a temporary memory stream
MemoryStream stream = new MemoryStream();
// Construct a serialization formatter that does all the hard work
BinaryFormatter formatter = new BinaryFormatter();
// This line is explained in the "Streaming Contexts" section
formatter.Context = new StreamingContext(StreamingContextStates.Clone);
// Serialize the object graph into the memory stream
formatter.Serialize(stream, original);
// Seek back to the start of the memory stream before deserializing
stream.Position = 0;
// Deserialize the graph into a new set of objects
// and return the root of the graph (deep copy) to the caller
return (formatter.Deserialize(stream));
}
What type of objects are you storing in your dictionary ?
You'll need to iterate over the content of the Dictionary and clone/duplicate the contents in some way.
If your object implements ICloneable you could do something like,
Dictionary<int, MyObject> original = new Dictionary<int, MyObject>();
... code to populate original ...
Dictionary<int, MyObject> deepCopy = new Dictionary<int, MyObject>();
foreach(var v in a)
{
MyObject clone = v.Value.Clone();
b.Add(v.Key, clone);
}
I can't tell from your question if dict is a dictionary of dictionaries? The simplest way to make a deep copy of a collection is to iterate through its members and clone each one.
If your value implements ICloneable:
OrderedDictionary newDict = new OrderedDictionary();
foreach(DictionaryEntry entry in OriginalDictionary)
{
newDict[entry.Key] = entry.Value.Clone();
}
If your values can't be Clone()d, you'll have to copy them another way.
OrderedDictionary newDict = new OrderedDictionary();
foreach(DictionaryEntry entry in OriginalDictionary)
{
MyClass x = new MyClass();
x.myProp1 = entry.Value.myProp1 as primitive value;
x.myProp2 = entry.Value.myProp2 as primitive value;
newDict[entry.Key] = x;
}

Reversing the save method in the Open method

Fairly new to programming. I just can't wrap my head around how to get this to work in reverse. I have a save method and an open method.
Save:
IDictionary<string, IDictionary<string, object>> pluginStates = new Dictionary<string, IDictionary<string, object>>();
signaller.RaiseSaveRequest(pluginStates); <--goes out and gets packed plugins
//loop through plugins to get values and types
//holds all of the plugin arrays
Dictionary<string, object> dictProjectState = new Dictionary<string, object>();
foreach (KeyValuePair<string,IDictionary<string,object>> plugin in pluginStates)
{
//holds jsonRepresented values
Dictionary<string, object> dictJsonRep = new Dictionary<string, object>();
//holds object types
Dictionary<string, object> dictObjRep = new Dictionary<string, object>();
object[] arrayDictHolder = new object[2]; //holds all of the dictionaries
string pluginKey = plugin.Key;
IDictionary<string, object> pluginValue = new Dictionary<string, object>();
pluginValue = plugin.Value;
foreach (KeyValuePair<string, object> element in pluginValue)
{
string jsonRepresentation = JsonConvert.SerializeObject(element);
object objType = element.Value.GetType().ToString();
dictJsonRep.Add(element.Key, jsonRepresentation);
dictObjRep.Add(element.Key, objType);
}
arrayDictHolder[0] = dictJsonRep;
arrayDictHolder[1] = dictObjRep;
dictProjectState.Add(pluginKey, arrayDictHolder);
}
JsonSerializer serializer = new JsonSerializer();
using (StreamWriter sw = new StreamWriter(strPathName))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, dictProjectState);
}
So, when someone saves, an event handler goes out and gets the packedState of each plugin, adds it to a dictionary pluginStates. I then go through each plugin in the pluginStates, adding the key, and json string version of the value to 1 dictionary and the key, object type to another dictionary, add those 2 dictionaries to an array and then pack up a dictionary that contains the pluginKey and the array for each plugin. Reasoning: when deserializing, I'm hitting problems going from JArray to type DataTable and other types that are within the dictionary that gets passed back to the plugin to unpack itself upon opening. I'm trying to figure out how to reverse this, so that when user opens project, I have the dictProjectState and need to bring it all the way back through the code to end up with 1 dictionary containing the plugins. How do I mirror this save in the open??
Thanks!
I believe when you deserialize in the open function, you should get an object that can be cast to Dictionary<string, object>.
The keys of this dictionary would be the names of the plugins in your final dictioary (pluginKey in yoru save method). The values should be the object[] arrayDictHolder objects. You can cast them to object[] and then get at the two values inside (dictJsonRep and dictObjRep in your save function).
Once those values are both cast to Dictionary<string, object>(), you can iterate through the KeyValuePairs in one of them, and use the key from those pairs to get at the value store in the other dictionary.
Code would be similar to the following:
var pluginStates = new Dictionary<string, IDictionary<string, object>>();
var dictProjectState = jsonSerializer.Deserialize(reader) as Dictionary<string, object>;
foreach(var projectStatePair in dictProjectState)
{
string pluginKey = projectStatePair.Key;
var pluginValues = new Dictionary<string, object>();
object[] arrayDictHolder = projectStatePair.Value as object[];
var dictJsonRep = arrayDictHolder[0] as Dictionary<string, object>;
var dictObjRep = arrayDictHolder[1] as Dictionary<string, object>;
foreach(var JsonRepPair in dictJsonRep)
{
string jsonRepresentation = JsonRepPair.Value;
// We know both dictionaries have the same keys, so
// get the objType for this JsonRepresentation
object objType = dictObjRep[JsonRepPair.Key];
// Since you're serializing objType to a string, you'll need
// to get the actual Type before calling DeserializeObject.
Type jsonType = System.Type.GetType(objType as string);
object value = JsonConvert.DeserializeObject( jsonRepresentation, jsonType );
pluginValues.Add( JsonRepPair.Key );
}
pluginStates.Add( pluginKey, pluginValues );
}
Note: You are currently setting objType to element.Value.GetType().ToString(); in your save function. You will either need to update save() to set it to element.Value.GetType(); or convert the type strings back to actual Types before trying to deserialize.
Note: I am not very familiar with the Json libraries. The above code is focused on creating your pluginStates dictionary out of the deserialized results. You will need to handle the creation of your JsonSerializer and make sure you close any streams after you've deserialized them. You may also need to tweak the actual deserialization calls to get the desired results.
Update: Added conversion of objType to actual System.Type object.

Categories

Resources