Software Development

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.

6 comments
joebarthib
joebarthib

Hi I know this post is old but if my comment may help people... I first implemented something like your example, deriving from List (where KeyValue is like KeyValuePair but has properties with setters so that they can be serialized), but then found an example of a class deriving from Dictionary and implementing IXMLSerializable: http://stackoverflow.com/questions/83232/is-there-a-serializable-generic-key-value-pair-class-in-net. Isnt' it more straightforward, without the look up issues mentioned. Of course it's a dictionary and not a hash table, but maybe worthy if one needs an xml-serializable dictionary rather than a hashtable in the first place or doesn't care?

rpb_
rpb_

Unless you are dealing with really small hashtables, I wouldn't have thought you'd want to do a linear search through a long list to find the item you're looking for. Wouldn't a better idea be to forget about implementing "public object this[string key]" and all that, and just use your list of pairs to do the persistence? So you would load your object from file and convert it to a hashtable, which then gets used (efficiently). When you want to save it to file again you convert it back to a list of pairs and save that.

kenny+tr
kenny+tr

MSDN clearly shows that Hashtable implements ISerializable and has the Serializable attribute: [SerializableAttribute] [ComVisibleAttribute(true)] public class Hashtable : IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback, ICloneable You can also check in object browser to confirm that Hashtable implements ISerializable. Not only that, but you can see that it implements the ISerializable.GetObjectData method and the protected constructor needed to deserialize. This has been true since the .NET framework was first released.

zs_box
zs_box

You are correct, the performance of a lookup on an OrderedTable won't be as fast as the performance a Hashtable will provide. Your idea about converting the Hashtable to a list of pairs is actually close to what the OrderedTable does if you simply use it as a persistence tool. There's nothing stopping you from converting the Hashtable to an OrderedTable, serializing it, then deserializing the OrderedTable and immediately extracting a Hashtable from it. If you're interested in "lighter" ways to serialize a Hashtable look here: http://msmvps.com/blogs/rakeshrajan/archive/2006/01/15/81105.aspx He has a good example of how to do it in a very lightweight manner.

zs_box
zs_box

Have you tried to serialize a hashtable? Or did you just want to post without trying it yourself? Try to execute this code: XmlSerializer serializer = new XmlSerializer(typeof(Hashtable)); Do you not get this message? "The type System.Collections.Hashtable is not supported because it implements IDictionary." Also do a Google search for "Serialize Hashtable". You'll see that objects that implement IDictionary are not as easy to serialize to XML as other objects. It's just not a really simple thing to do.

kenny+tr
kenny+tr

Of course I have serialized a Hashtable before. I have never had to use any tricks, just pass a Hashtable instance to the Serialize method of an instance of BinaryFormatter. You can do the same with the SoapFormatter. Serialization is not necessarily XML serialization. The way this article reads, Hashtables are not serializable at all. And no, I have never tried to use XmlSerializer with a Hashtable, but I have used the BinaryFormatter and I understand that you can use the SoapFormatter. It did not seem obvious from the article that it was talking about a corner case. How to serialize a Hashtable? Use the BinaryFormatter. How to use XmlSerializer with a Hashtable? Use the code from this article.

Editor's Picks