Developer

How do I... Serialize a hash table in C# when the application requires it?

<img src="http://t.cbsimg.net/i/z/200606/how_110x85.jpg" align="right" border="0" height="85" hspace="5" vspace="5" width="110" />Unfortunately the <a href="http://www.techrepublic.com/search/index.php?t=1&amp;s=0&amp;o=1&amp;q=.net+framework" target="_blank">.NET Framework</a> does not allow serialization of any object that implements the <em>IDictionary</em> interface. This restriction includes, but is not limited to, hash tables. I, like many developers have run into this restriction head-on and been disappointed about the inability for serialization on hash tables. There are many ways to approach this problem -- some prefer to simply find an alternative to <a href="http://www.techrepublic.com/search/index.php?t=11&amp;s=0&amp;o=1&amp;q=%22hash+tables%22" target="_blank">hash tables</a>, others implement the IXmlSerializable interface in a custom object and wrap it around the hash table. My approach is to use a generic <em>List&lt;T&gt;</em> object as a base and implement the standard features used in a hash table within that object. The <em>List&lt;T&gt;</em> derivative can then be serialized.

Unfortunately, the .NET Framework does not allow serialization of any object that implements the IDictionary interface. This restriction includes, but is not limited to, hash tables. Like many developers, I have run into this restriction head on and been disappointed about the inability for serialization on hash tables.

There are many ways to approach this problem — some prefer to simply find an alternative to hash tables, others implement the IXmlSerializable interface in a custom object and wrap it around the hash table. My approach is to use a generic List<T> object as a base and implement the standard features used in a hash table within that object. The List<T> derivative can then be serialized.

This blog post is also available in PDF form as a TechRepublic download, which includes a sample Visual Studio project file exploring the coding principles outlined.

Using List<T> as a base

List<T> objects are serializable, and this serialization is automatically handled in the .NET Framework. This means we will not have to implement our own serialization routines, and it's one of the reasons I decided to use List<T> as a base for my solution.

Using List<T> also provides you with some "free" functionality as a result of inheritance:

  • Automatically keeps track of the number of entries
  • Gives us the ability to use the Find methods (FindAll, FindLast, Find, etc....)
  • Gives us the ability to use the Exists method
  • Gives us the ability to use Foreach() looping
  • Gives us the ability to use Sorting (and the ability to guarantee the sorting of objects within the list)

So obviously, the List<T> object gives us a nice start. However, there are still a few methods we need to implement within our object so that we have the basic hash table functionality:

  • An Add(key, value) method, which allows you to add values to the table
  • The ability to pull a value via a key (like the hash table's indexer)
  • The ability to remove a value via a key
  • The ability to see if a key already exists or not

There are also a few other features that will make using this object easier:

  • A constructor that accepts a hash table and enumerates through the hash table to populate the object
  • A property that returns a hash table

With that list of features, we now have an object that supports the most commonly used hash table functions, can be translated automatically to/from a hash table, and is also serializable. I chose to call this object the OrderedTable.

The OrderedTable object's code

The first step in coding an OrderedTable is to inherit from the List<T> object and mark the object as serializable (See Listing A).

Listing A

    [Serializable()]

public class OrderedTable : List<NameValuePair>

{

//Methods/Properties here...

}

You'll notice that this inherits from NameValuePair, which is a simple object with two properties — Name and Value. This object allows us to store/access the List's data in a similar way that a hash table does.

The next step will be to create a constructor that accepts a hash table and converts it into an OrderedTable. The code for this constructor is shown Listing B.

Listing B

   public OrderedTable(hash table table)

{

IDictionaryEnumerator enumerator = table.GetEnumerator();

while (enumerator.MoveNext())

this.AddIfNotExisting(Convert.ToString(enumerator.Key),

enumerator.Value);

}

This function uses the GetEnumerator method on the hash table object to get a reference to an IDictionaryEnumerator interface. The IDictionaryEnumerator interface is then used to loop through the hash table's data. Each key/value pair in the hash table is then added to the OrderedTable.

At this point, we need to create an Add() method that can add data into our OrderedList. The code for this is shown in Listing C.

Listing C

    public void Add(string key, object value)

{

base.Add(new NameValuePair(key, value));

}

This method simply accepts a key/value and then creates a NameValuePair object to add to the OrderedTable. You'll notice that this method uses base.Add — the reason for this is that we want to add the NameValuePair object to the base List<T> object.

The indexer on the OrderedTable accepts a key and returns the value associated with that key. This is similar to how the hash table's indexer works. The code for this is shown Listing D.

Listing D

    public object this[string key]

{

get

{

object valueObject = null;

NameValuePair pair = this.Find(

delegate(NameValuePair nvp)

{

return nvp.Name == key;

});

if (pair != null)

valueObject = pair.Value;

return valueObject;

}

set

{

NameValuePair pair = this.Find(

delegate(NameValuePair nvp)

{

return nvp.Name == key;

});

if (pair != null)

pair.Value = value;

}

}

This property uses the Find() functions as mentioned above to execute a delegate against the objects (NameValuePairs) stored within the OrderedTable. After the correct object is found, we either set the value or return the value depending on which accessor we're in.

The last important property in the OrderedTable class is the HashTable property. This property takes care of the work of converting the OrderedTable back into a hash table. The code is shown in Listing E.

Listing E

    public HashTable HashTable

{

get

{

HashTable table = new HashTable();

foreach (NameValuePair pair in this)

if (!table.ContainsKey(pair.Name))

table.Add(pair.Name, pair.Value);

return table;

}

}

This property loops through the current OrderedTable and adds objects to a hash table for each object in the OrderedTable. It then returns the fully populated hash table.

Serialization example

Listing F is an example of first creating a hash table, converting it to an OrderedTable, serializing the OrderedTable, and deserializing the OrderedTable to give us access again to the hash table.

Listing F

    //Create the hashtable.

HashTable HashTable = new HashTable();

//Add some data.

HashTable.Add("Integer", 1);

HashTable.Add("String", "Test");

HashTable.Add("DateTime", DateTime.Now);

//Create the OrderedTable using the constructor

// that accepts a hashtable.

OrderedTable orderedTable = new OrderedTable(HashTable);

//Create a XmlSerializer for the OrderedTable

XmlSerializer serializer = new XmlSerializer(typeof(OrderedTable));

//Serialize the OrderedTable to OrderedTable.xml

using (StreamWriter writer = new StreamWriter(@"c:orderedtable.xml"))

{

serializer.Serialize(writer, orderedTable);

}

//Create a new hashtable

HashTable newTable = null;

//Deserialize the OrderedTable

using (StreamReader reader = new StreamReader(@"C:orderedtable.xml"))

{

OrderedTable tempTable = (OrderedTable)serializer.Deserialize(reader);

newTable = tempTable.hash table;

}

//newTable is now fully populated.
Listing G shows what the OrderedTable.xml looks like after the OrderedTable is serialized.

Listing G

<?xml version="1.0" encoding="utf-8"?>

<ArrayOfNameValuePair

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<NameValuePair>

<Name>DateTime</Name>

<Value xsi:type="xsd:dateTime">2007-09-12T16:17:59.3267731-04:00</Value>

</NameValuePair>

<NameValuePair>

<Name>String</Name>

<Value xsi:type="xsd:string">Test</Value>

</NameValuePair>

<NameValuePair>

<Name>Integer</Name>

<Value xsi:type="xsd:int">1</Value>

</NameValuePair>

</ArrayOfNameValuePair>

More information

For more information and the full source code for the OrderedTable object, check out the sample application included with the download version of this document.

Editor's Picks