Introduction
Dictionary
by default is not XML serializable.To make it XML serializable with XML serializer, we need to implement IXmlSerialiable
. But also derived class of Dictionary
is not Binary deserializable so here I have given a solution with a Dictionary
which is both XML and binary serializable.
Background
DataContractSerializer
can serializeDictionary<,>
into XML, but we have old code where XML serializer is used and current implementation does not support DataContractSerializer
. I needed to use binary serialization for deep copy and also needed XML serialization for other proposes. So I have implemented a dictionary
which supports any type of key and value and also can be serialized in XML and also binary format.
Implementation
For making it serializable, we need to implement a derived class which implements interface IXmlSerializable
. Also, to get all functionality of Dictionary
, I have implemented that class from Dictionary
class. Here both key and value is generic as Dictionary
.
[XmlRoot("Dictionary")]
public class SerializableDictionary<KT, VT>:Dictionary<KT ,VT>,IXmlSerializable
IXmlSerializable
class contains the following method that we needed to implement in our class.
public interface IXmlSerializable
{
XmlSchema GetSchema();
void ReadXml(XmlReader reader);
void WriteXml(XmlWriter writer);
}
But we also have to make our dictionary binary serializable. For serialization, we need to set serializable attribute over our derived class. However, when we try to deserialize our derived class in raise error saying there is no appropriate contractor to deserialize our class though default constructor exists. Dictionary<>
class implements its own custom serialization using ISerializable
so our derived class needs special constructor for deserialization.
public SerializableDictionary(SerializationInfo info,
StreamingContext context):base(info,context)
{
}
So now this class is binary serializable. But for XML serialization, we also need to store type of key and value for deserialization. In WriteXml
function of IXmlSerializable
, we can organize how Dictionary
will be converted into XML format. In my code:
public void WriteXml(System.Xml.XmlWriter writer)
{
for (int i=0;i<Keys.Count;i++)
{
KT key =Keys.ElementAt(i);
VT value= this.ElementAt(i).Value;
writer.WriteStartElement("Item");
writer.WriteStartElement("Key");
writer.WriteAttributeString(string.Empty, "type",
string.Empty, key.GetType().AssemblyQualifiedName);
new XmlSerializer(key.GetType()).Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
if (value != null)
{
writer.WriteAttributeString(string.Empty, "type",
string.Empty, value.GetType().AssemblyQualifiedName);
new XmlSerializer(value.GetType()).Serialize(writer, value);
}
writer.WriteEndElement();
writer.WriteEndElement();
}
}
In the code, you can see that it first creates an element <Item>
for each dictionary
item and under which creates <Key>
element and also stores type of key of dictionary as attribute. Then it serializes the value of key inside this element. After that, it also stores the <value>
element under <item>
and also stores type
as attribute. As same as key
, it also serializes the value
part of dictionary. I did not consider null
value in my code but thanks to Don Kackman. With his feedback, I have changed my implementation. In that, I again do not allow null
in key
but in value
if value
is null
then it will not serialize the value
and just write in XML like empty element as "<value/>
".
We we see the output of SerializableDictionary<int,string>
of two elements, then it would look like:
?="1.0"="utf-8"
<Dictionary>
<Item>
<Key type="System.Int32, mscorlib, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089">
<int>1</int>
</Key>
<value type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">
<string>Milton</string>
</value>
</Item>
<Item>
<Key type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">
<int>2</int>
</Key>
<value type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">
<string>Bashar</string>
</value>
</Item>
</Dictionary>
Now when we deserialize the serialized value of Dictionary<int,string>
, then it will use ReadXml
of IXmlSerliazable
. For deserialization, the first key type is retrieved and after that key is deserialized with the given type. Also value of dictionary
is retrieved in the same way. Here as value can be null
so for empty value XML element, it returns default value of that type.
public void ReadXml(XmlReader reader)
{
Boolean wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
{
return;
}
while (reader.NodeType != XmlNodeType.EndElement)
{
KT key ;
if (reader.Name == "Item")
{
reader.Read();
Type keytype = Type.GetType(reader.GetAttribute("type"));
if (keytype != null)
{
reader.Read();
key = (KT)new XmlSerializer(keytype).Deserialize(reader);
reader.ReadEndElement();
Type valuetype = (reader.HasAttributes)?Type.GetType
(reader.GetAttribute("type")):null;
if (valuetype != null)
{
reader.Read();
Add(key, (VT)new XmlSerializer
(valuetype).Deserialize(reader));
reader.ReadEndElement();
}
else
{
Add(key, default(VT));
reader.Skip();
}
}
reader.ReadEndElement();
reader.MoveToContent();
}
}
reader.ReadEndElement();
}
I have tested my implementation with both simple and complex types. It could have been possible to have a Dictionary
which also contains a property which is also dictionary
type. In that case, the WriteXml
and ReadXml
will also be called again when the dictionary
type property needs to be serialized and deseralized.
Conclusion
My implemented Dictionary
class supports both XML serializable by XmlSerializer
and also supports binary serialization and key and value also support any types. In my attachment, I also have a demo of both generic implementation of both binary deep copy and XML and binary serialization. I have tried to create an complex dictionary
implementation and have seen it works well and deserializes into the same values.