Monday, August 12, 2013

Persisting A Dictionary By Serialization

I recently helped out a guy on the MSDN forums who wanted to be able to persist his data to an XML file, to be able to be retrieved and restored by his application at application startup. He needed to store his data in a Dictionary. All sounds good so far except he couldn’t figure a good way to serialize the data to XML and then deserialize it in order to load it into a Dictionary.

When I first read his post, my first thought was to serialize the entire dictionary to XML, rather than try and serialize just the data parts. But guess what … a Dictionary cannot be serialized with the XmlSerializer. But, there is a way to do it, and that’s with the DataContractSerializer … turns out that it works just fine! And we don’t even have to decorate our Dictionary with a [DataContract] attribute. Let’s see how to do that:

First, let’s get some test data loaded in a Dictionary:

Dictionary<string, List<string>> Layers = new Dictionary<string, List<string>>();
Layers.Add("ourlayer1", new List<string>());
Layers["ourlayer1"].Add("somerandomthing");
Layers["ourlayer1"].Add("anotherrandomthing");

Layers.Add("ourlayer2", new List<string>());
Layers["ourlayer2"].Add("whatisthis");
Layers["ourlayer2"].Add("whoisthis");

Next, we’ll serialize the Layers Dictionary and write it out to an XML file.

DataContractSerializer dc = new DataContractSerializer(typeof(Dictionary<string, List<string>>));

using (StringWriter sw = new StringWriter())
using (var writer = new XmlTextWriter(sw))
{
    writer.Formatting = Formatting.Indented; // indent the Xml so it's human readable
    dc.WriteObject(writer, Layers);
    writer.Flush();
    File.WriteAllText("Layers.xml", sw.ToString());
}

Here’s what the Layers.xml file looks like:

<ArrayOfKeyValueOfstringArrayOfstringty7Ep6D1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <KeyValueOfstringArrayOfstringty7Ep6D1>
    <Key>ourlayer1</Key>
    <Value>
      <string>somerandomthing</string>
      <string>anotherrandomthing</string>
    </Value>
  </KeyValueOfstringArrayOfstringty7Ep6D1>
  <KeyValueOfstringArrayOfstringty7Ep6D1>
    <Key>ourlayer2</Key>
    <Value>
      <string>whatisthis</string>
      <string>whoisthis</string>
    </Value>
  </KeyValueOfstringArrayOfstringty7Ep6D1>
</ArrayOfKeyValueOfstringArrayOfstringty7Ep6D1>

And, last but not least, let’s read the file back into the Dictionary:

Layers.Clear();
string s = File.ReadAllText("Layers.xml");
using (StreamReader sr = new StreamReader("Layers.xml"))
{
    Layers = dc.ReadObject(sr.BaseStream) as Dictionary<string, List<string>>;
}

Take a look at your Layers Dictionary in the debugger when this process is done and you’ll see it’s been reserialized correctly. It’s a pretty easy process I think. Here’s the link to the MSDN forum question, if you’re interested:

http://social.msdn.microsoft.com/Forums/en-US/3f1897ab-12a3-4da3-acfe-4b08c2cd74db/trying-to-write-a-simple-program-to-gather-strings-and-save-in-xml-file