One Way to Serialize Dictionaries

If you try to use XML Serialization on anything that implements IDictionary, and you’ll get an exception telling you it can’t be serialized. This question comes up, time and time again. This post will present one way to do it, using a “proxy” class.

The Code

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Serialization;

/// <summary>
/// Proxy class to permit XML Serialization of generic dictionaries
/// </summary>
/// <typeparam name="K">The type of the key</typeparam>
/// <typeparam name="V">The type of the value</typeparam>
public class DictionaryProxy<K, V>
{
    #region Construction and Initialization
    public DictionaryProxy(IDictionary<K, V> original)
    {
        Original = original;
    }

    /// <summary>
    /// Default constructor so deserialization works
    /// </summary>
    public DictionaryProxy()
    {
    }

    /// <summary>
    /// Use to set the dictionary if necessary, but don't serialize
    /// </summary>
    [XmlIgnore]
    public IDictionary<K, V> Original { get; set; }
    #endregion

    #region The Proxy List
    /// <summary>
    /// Holds the keys and values
    /// </summary>
    public class KeyAndValue
    {
        public K Key { get; set; }
        public V Value { get; set; }
    }

    // This field will store the deserialized list
    private Collection<KeyAndValue> _list;

    /// <remarks>
    /// XmlElementAttribute is used to prevent extra nesting level. It's
    /// not necessary.
    /// </remarks>
    [XmlElement]
    public Collection<KeyAndValue> KeysAndValues
    {
        get
        {
            if (_list == null)
            {
                _list = new Collection<KeyAndValue>();
            }

            // On deserialization, Original will be null, just return what we have
            if (Original == null)
            {
                return _list;
            }

            // If Original was present, add each of its elements to the list
            _list.Clear();
            foreach (var pair in Original)
            {
                _list.Add(new KeyAndValue { Key = pair.Key, Value = pair.Value });
            }

            return _list;
        }
    }
    #endregion

    /// <summary>
    /// Convenience method to return a dictionary from this proxy instance
    /// </summary>
    /// <returns></returns>
    public Dictionary<K, V> ToDictionary()
    {
        return KeysAndValues.ToDictionary(key => key.Key, value => value.Value);
    }
}

Construction and Initialization

XML Serialization requires that the class being serialized have a default constructor. But in this case, the Original dictionary is not set. Making the Original property public and read/write permits the caller to set the dictionary after construction but before serialization.

The Proxy List

The data that are actually serialized and deserialized are a list of key/value pairs. I could have chosen to use the System.Collections.Generic.KeyValuePair type, but I wanted control over serialization, so I created my own simple KeyAndValue type.

To understand the details of how the KeysAndValues property works, you have to understand how XML Serialization handles collections. On serialization, nothing very special happens. We create an empty collection, fill it from the Original dictionary, then return the filled collection. The XML Serializer then enumerates the elements in the list and serializes each one.

It’s a different story on deserialization. First, an instance of the DictionaryProxy class is created, using the default (parameterless) constructor. Note that this leaves Original set to null. The KeysAndValues property getter is then called. It finds _list to be null, so creates an empty collection. It finds Original still set to null, so just returns _list.

However, the XML Serializer then takes the collection we just returned (_list), deserializes each KeyAndValue item it finds, then adds them to the collection. Since we first stored a reference to that collection in _list, the XML Serializer is filling our internal list from the deserialized elements.

Note that I originally made a common mistake, and used a local variable instead of the field, _list. The result was that I returned this local variable, the XML Serializer filled the collection in the local variable, I returned the local variable with the filled elements, and the next time KeysAndValues was called, the filled collection is empty because we threw away the local variable that was filled!

ToDictionary

A simple method to turn the proxy type into a real dictionary is also provided. It takes advantage of the LINQ ToDictionary extension method. The particular overload of this method that was used accepts two delegates. The first translates a KeyAndValue instance into a key, and one that translates a KeyAndValue instance into a value. These are simple enough that I included them inline, as lambda expressions.

Next Steps

Although I haven’t tested it, I also created a class to serialize Hashtables:

public class HashTableProxy : DictionaryProxy<object, object>
{
    public HashTableProxy(Hashtable original)
    {
        var temp = new Dictionary<object, object>(original.Count);
        foreach (DictionaryEntry entry in original)
        {
            temp.Add(entry.Key, entry.Value);
        }

        Original = temp;
    }
}

I also want to note that I could also have implemented IXmlSerializable on this class instead of depending on the XML Serializer to produce the desired XML.

Technorati Tags:

About these ads
This entry was posted in Serialization. Bookmark the permalink.

6 Responses to One Way to Serialize Dictionaries

  1. John says:

    I happen to like it better because it\’s easier to maintain. Many people have trouble with XmlReader and XmlWriter, so I wouldn\’t want to implement IXmlSerializable unless I had to. This way, serialization, deserialization, and schema creation are all handled by .NET.

  2. Matt says:

    Excellent. Can you provide a simple example of creating a KeyAndValue item and *adding* it to the DictionaryProxy? I got lost in the syntax after trying to do this. (I was trying to avoid converting .ToDictionary.) Thanks.

  3. John says:

    @Matt: Actually, the idea is that you\’d do your Add, Remove and other Dictionary operations on the original dictionary. The dictionary proxy class is only meant to be used for serialization and deserialization.

  4. Pingback: Why isn’t there an XML-serializable dictionary in .NET? | Everyday I'm coding

  5. Mar says:

    Ok, but how are you using this proxy? Say you have User class (.Name, .Address, .Settings) that will be xml serialized. The .Settings property is a Dictionary. Would I change the type over to DictionaryProxy and then access User.Settings.Original throughout my app? I was hoping to have consumer of the User class to just worry about accessing the Dictionary and not really be involved with the proxy. Ideas?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s