Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Both XML and Binary Serializable Dictionary

4.67/5 (12 votes)
3 Jan 2011CPOL3 min read 55.9K   1.4K  
Created a derived class from Dictionary which is both XML and binary serializable

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.

C#
[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.

C#
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.

C#
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:

C#
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;
               //create <item>
               writer.WriteStartElement("Item");
               //create <key> under <item>
               writer.WriteStartElement("Key");
               writer.WriteAttributeString(string.Empty, "type",
       string.Empty, key.GetType().AssemblyQualifiedName);
               new XmlSerializer(key.GetType()).Serialize(writer, key);
               //end </key> element
               writer.WriteEndElement();
               //create <value> under <item>
               writer.WriteStartElement("value");
               if (value != null)
               {
                   writer.WriteAttributeString(string.Empty, "type",
           string.Empty, value.GetType().AssemblyQualifiedName);
                   new XmlSerializer(value.GetType()).Serialize(writer, value);
               }
               //end </value>
               writer.WriteEndElement();
               //end </item>
               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:

XML
?<?xml version="1.0" encoding="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.

C#
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)