Dzisiejszego dnia potrzebowałem użyć serializacji XML dla klasy Dictionary. Bardzo się zdziwiłem gdy okazało się, że klasa ta nie wspiera tego rodzaju serializacji. Wydawałoby się, że zapis danych w formacie XML jest dzisiaj już standardem, ale okazuje się, że jednak nie wszędzie. Stosując podstawową zasadę programisty po co pisać coś co ktoś inny już napisał uruchomiłem Google i zacząłem szukać rozwiązania. Udało mi się znaleźć kilka przykładów rozwiązania tego problemu, ale każde z nich miało jakiś drobny błąd, który powodował, że słownik nie działał tak jak powinien.

Po chwili zabawy udało mi się uzyskać rozwiązanie, w którym na razie nie znalazłem jeszcze błędów. Poniżej zamieszczam kod, może komuś się przyda. Klasa SerializableDictionary serializuje obiekt Dictionary do formatu binarnego, jak i do XML.

using System;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Globalization;

namespace LicenseCommon
{
  [Serializable]
  public class SerializableDictionary<TKey, TVal> : Dictionary<TKey, TVal>, IXmlSerializable, ISerializable
  {
    #region Private Properties
    protected XmlSerializer ValueSerializer
    {
      get { return _valueSerializer ?? (_valueSerializer = new XmlSerializer(typeof(TVal))); }
    }

    private XmlSerializer KeySerializer
    {
      get { return _keySerializer ?? (_keySerializer = new XmlSerializer(typeof(TKey))); }
    }
    #endregion
    #region Private Members
    private XmlSerializer _keySerializer;
    private XmlSerializer _valueSerializer;
    #endregion

    #region Constructors
    public SerializableDictionary()
    {
    }

    public SerializableDictionary(IDictionary<TKey, TVal> dictionary) : base(dictionary)
    {
    }

    public SerializableDictionary(IEqualityComparer<TKey> comparer) : base(comparer)
    {
    }

    public SerializableDictionary(int capacity) : base(capacity)
    {
    }

    public SerializableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer)
    {
    }

    public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer)
    {
    }

    #endregion
    #region ISerializable Members
    protected SerializableDictionary(SerializationInfo info, StreamingContext context)
    {
      int itemCount = info.GetInt32("itemsCount");
      for (int i = 0; i < itemCount; i++)
      {
        KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format(CultureInfo.InvariantCulture, "Item{0}", i), typeof(KeyValuePair<TKey, TVal>));
        Add(kvp.Key, kvp.Value);
      }
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("itemsCount", Count);
      int itemIdx = 0;
      foreach (KeyValuePair<TKey, TVal> kvp in this)
      {
        info.AddValue(String.Format(CultureInfo.InvariantCulture, "Item{0}", itemIdx), kvp, typeof(KeyValuePair<TKey, TVal>));
        itemIdx++;
      }
    }
    #endregion

    #region IXmlSerializable Members
    void IXmlSerializable.WriteXml(XmlWriter writer)
    {
      foreach (KeyValuePair<TKey, TVal> kvp in this)
      {
        writer.WriteStartElement("item");
        writer.WriteStartElement("key");
        KeySerializer.Serialize(writer, kvp.Key);
        writer.WriteEndElement();
        writer.WriteStartElement("value");
        ValueSerializer.Serialize(writer, kvp.Value);
        writer.WriteEndElement();
        writer.WriteEndElement();
      }
    }

    void IXmlSerializable.ReadXml(XmlReader reader)
    {
      if (reader.IsEmptyElement)
      {
        return;
      }
      // Move past container
      if (reader.NodeType == XmlNodeType.Element && !reader.Read())
      {
        throw new XmlException("Error in Deserialization of SerializableDictionary");
      }
      while (reader.NodeType != XmlNodeType.EndElement)
      {
        reader.ReadStartElement("item");
        reader.ReadStartElement("key");
        TKey key = (TKey)KeySerializer.Deserialize(reader);
        reader.ReadEndElement();
        reader.ReadStartElement("value");
        TVal value = (TVal)ValueSerializer.Deserialize(reader);
        reader.ReadEndElement();
        reader.ReadEndElement();
        Add(key, value);
        reader.MoveToContent();
      }
      // Move past container
      if (reader.NodeType == XmlNodeType.EndElement)
      {
        reader.ReadEndElement();
      }
      else
      {
        throw new XmlException("Error in Deserialization of SerializableDictionary");
      }
    }

    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
    {
      return null;
    }
    #endregion
  }
}