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:

This entry was posted in Serialization. Bookmark the permalink.

7 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. 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?

  5. Jose says:

    Hi John,

    Im using this in a class that is consuming a WebService, the serialization went well and I have something like this:

    0.10
    500

    0.1110
    450

    0.12
    400

    The problem is In the client project I don’t know how to deserialize it, I copyed the same DictionaryProxy class but this call:

    webService.GetMyDictionary() always fails in the casting, it keeps telling me:

    public Dictionary GetDictionary()
    {
    DictionaryProxy result = new DictionaryProxy();

    // Im not able to find a workaround to set this, i tried with a “set” in the KeysValues
    // property, but i dont really know hot to consume this
    result = _wsTransactional.GetDictionary();

    return result.ToDictionary();
    }

    can you help me with this?

    Regards

Leave a reply to Mar Cancel reply