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
}
}


Two years later, I still haven’t found a way better than this one to serialize a Dictionary in .NET.
Thanks so much!
Very nice code. Can I use it for my commercial project? It is sold as code, and I would certainly include a link to this page.
It it no problem. You can use this code in commercial code. It would be nice if you can put somewhere link to this page.
Ok I resolved!
It’s an official bug, this article helped me to resolve:
http://support.microsoft.com/kb/815131
Nice to hear it. Sorry I didn’t replay for your post but I suffer on lack of time in last time. 🙂
And how can I transfer this SerializableDictionary into my web service?
I added this class to my web service project, created there simple function
[WebMethod]
public string HelloWorld(SerializableDictionary s)
{
return "Hello World M!";
}
And when I try to call this method from client application, intellisense shows me this type as DataSet, not SerializableDictionary.
What am I doing wrong?
Thanks a lot. very good code.
Yes you are right. I didn’t notice that translation plugin also was translating source code. I have changed it and now everything should be correct in other languages than Polish.
Thanks for this! This has helped me serialize other things.
A few syntax errors:
protected SerializableDictionary(SerializationInfo info, StreamingContext context)
{
int itemCount = info.GetInt32(„itemsCount”);
for (int in = 0; in < itemCount; in++)
{
KeyValuePair KVP = (KeyValuePair)info.GetValue(String.Format(CultureInfo.InvariantCulture, „Item{0}”, in), typeof(KeyValuePair));
Add(KVP.Key, KVP.Value);
}
}
„in” is now a keyword, so you’ll have to use something else like „i”
System.Xml.Scheme.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
System.Xml.Scheme <– should be Schema