Miscellaneous Debris

Avner Kashtan's Frustrations and Exultations
Serializing Dictionaries

Dictionaries have been an annoyance when serializing for a while now. IDictionary in v1.1 and now IDictionary<K,V> in v2.0 are both non-serializable, forcing us to find workarounds or use DataTables or any number of unsatisfactory solutions.

My guess is that is has something to do with the whole buckets/hashcode implementation. If we serialize a dictionary and then deserialize it on a different machine or process, the keys might have a different hashcode - the default hashcode for a reference type is its reference pointer, AFAIK - and the dictionary will be corrupted.

A quick solution for this is to transform the dictionary into a collection of KeyValuePairs, which is a non-hashed straightforward array, and serialize and deserialize that. This means the hashing algorithm runs again when we deserialize, building the new dictionary from scratch.

Usage is then simple:

DictionarySerializer<string, int> serializer = new DictionarySerializer<string,int>();
serializer.Serialize(myDictionary, myStreamWriter);

When implementing this, it turned out that while KeyValuePair<K,V> is marked as Serializable, it actually isn't possible to meaningfully serialize it. This is due to its Key and Value members being read-only, and the XmlSerializer only serializing public members. (More information here).
Due to this, I simply implemented my own internal SerializableKeyValuePair<K,V> struct and use that instead.
The Serialize method now looks something like this:

public void Serialize (IDictionary<K,V> dictionary, StreamWriter serializationStream)
{
   List<SerializableKeyValuePair<K,V>> dictionaryItems = GetKeyValueList(dictionary);
   XmlSerializer ser = new XmlSerializer(typeof(List<SerializableKeyValuePair<K,V>>));
   ser.Serialize(serializationStream, dictionaryItems);
}

Deserialization will follow logically:

public IDictionary<K,V> Deserialize (StreamReader serializationStream)
{
   XmlSerializer ser = new XmlSerializer(typeof(List<SerializableKeyValuePair<K,V>>));
   List<SerializableKeyValuePair<K,V>> dictionaryItems = ser.Deserialize(serializationStream) as List<SerializableKeyValuePair<K,V>>;

   IDictionary<K,V> dictionary = new Dictionary<K,V>(dictionaryItems.Count);
   foreach (SerializableKeyValuePair<K,V> item in dictionaryItems)
   {
      dictionary.Add(item.Key, item.Value);
   }
   
   return dictionary;
}

The problem with making this totally generic is that the serializer can receive a Stream, a TextWriter or an XmlWriter - three classes that do not share any common ancestor. This means we have several overloads doing exactly the same only with different types. A shame.

I've attache the full class for this, slightly more refactored than what I have here. Enjoy.

Published Tuesday, May 23, 2006 2:18 PM by AvnerK
Filed under: ,

Comments

# Interesting Finds: May 23, 2006 AM edition@ Tuesday, May 23, 2006 10:12 AM

Jason Haley

# re: Serializing Dictionaries@ Tuesday, May 23, 2006 3:01 PM

Paul Welter also created a nice solution:

http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx

Ward Bekker

# re: Serializing Dictionaries@ Wednesday, June 07, 2006 7:14 AM

How would you serialize Dictionary<K, V, T> where V: List<T>?
Thanks

Yaacov

# re: Serializing Dictionaries@ Thursday, June 08, 2006 2:29 AM

I'm not sure I understood your question.
The generic Dictionary class accepts two generic arguments. Are you asking about a scenario like Dictionary<string, List<int>>? Where one of my generic arguments is a generic argument in itself?

I wouldn't, basically. I would let the List<T> do its own serialization.

AvnerK

# re: Serializing Dictionaries@ Thursday, June 22, 2006 3:02 AM

I just used this.  Thanks so much!

Kevin

# WCF Serialization Part 2: NameValueCollection Denied!@ Wednesday, August 02, 2006 8:44 AM

As we all know, IDictionaries aren&#39;t serializable. This is has been a cause of much concern and consternation

Miscellaneous Debris

# re: Serializing Dictionaries@ Thursday, August 03, 2006 11:07 AM

Works like a charm thanks!!!

Chris McKelt

# re: Serializing Dictionaries@ Thursday, August 03, 2006 11:07 AM

Works like a charm thanks!!!

Chris McKelt

# re: Serializing Dictionaries@ Monday, May 21, 2007 11:06 AM

Thanks mate - appreciate this.  

Faxedhead

# ¿Goodbye Dictionary?@ Tuesday, July 15, 2008 4:07 AM

Generics La aparición del framework 2.0 nos trajo una grata sorpresa: La aparición de Generics, que nos

El blog de LluĂ­s Franco en Geeks.ms

# re: Serializing Dictionaries@ Wednesday, July 16, 2008 1:39 AM

Thanks! Very useful...

Markus

# ¿Goodbye Dictionary?@ Wednesday, July 16, 2008 4:01 AM

Corrección al artículo (16/07/2008): Todas las mediciones de tiempo efectuadas en las compartivas entre

El blog de Lluis Franco

# ¿Goodbye Dictionary?@ Wednesday, July 16, 2008 4:01 AM

Corrección al artículo (16/07/2008): Todas las mediciones de tiempo efectuadas en las compartivas entre

400 Bad Request

# re: Serializing Dictionaries@ Friday, November 07, 2008 4:41 AM

Why are you trying to (de)serialize IDictionary<K,V> when .NET has a perfectly serializable class Dictionary<K,V>??

Martin

# re: Serializing Dictionaries@ Friday, November 07, 2008 6:03 AM

Ignore previous comment, anything derived from IDictionary apparently cannot be (de)serialized.

Another idea for refactoring though: the serializable key value pair struct could be implemented without generic parameters because the encapsulating class already specifies them, thus the struct could simply be non-generic like this:

       public struct SerializableKeyValuePair

       {

           public K Key;

           public V Value;

           public SerializableKeyValuePair(KeyValuePair<K, V> kvp)

           {

               this.Key = kvp.Key;

               this.Value = kvp.Value;

           }

       }

Martin

# re: Serializing Dictionaries@ Tuesday, December 16, 2008 8:31 AM

Thanks, works really welll :)

sandy

Leave a Comment

(required) 
(required) 
(optional)
(required)