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

# re: Serializing Dictionaries@ Friday, October 23, 2009 1:58 PM

I think there might be some updates here for .NET 3.5. I am having no problem with a serialized Dictionary<int, List<int>> that I am using in WCF. I think the difference might be a different serializer. When using a DataContract, the NetDataContractSerializer is being used by default. Let's put it this way. I got the values on the server, passed them back through WCF and I got what I started with no problem. The class is not marked Serializable, it is marked DataContract and compiliation does not complain either.

So this is probably not an issue with WCF if you don't need to mark a class Serializable as well. This may work with a BinaryFormatter for serialization as well.

Wray

Leave a Comment

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