In an earlier post here, I wrote two functions to Serialize and Deserialize objects to XML strings.
Since then, while reading a book about XML, I learned of even more power in the .NET XML Serialization libraries*. In addition to serializing objects and collections of objects, you can serialize ArrayLists
which can contain disparate or non-homogeneous objects. Techno-babble translation: Disparate and non-homogenous mean 'different'.
*BTW: Never stop learning or you'll end up as geek road-kill on the nerd highway.
I wrote two functions (in XMLUtils
namespace) to encapsulate the logic. What do these functions do? Why, I'd be delighted to show you. Here's an example of the functions in action:
ArrayList MyArrayList = new ArrayList();
MyArrayList.Add(new MyNameSpace.MyClass("Sue"));
MyArrayList.Add(DateTime.Now);
MyArrayList.Add(new MyClass("Bob"));
MyArrayList.Add(1234);
Response.Write("----- Before serialization ------ <br />");
foreach (Object O in MyArrayList)
Response.Write(O.ToString() + "<br />");
String MyXml = XMLUtils.SerializeArrayList(MyArrayList);
MyArrayList = XMLUtils.DeSerializeArrayList(MyXml);
Response.Write("----- After deserialization ------<br />");
foreach (Object O in MyArrayList)
Response.Write(O.ToString() + "<br />");
The example shows an ArrayList
being created with a custom class, a DateTime
and an integer…disparate, non-homogeneous objects.
The ArrayList
is then serialized into an XML string.
The XML string is then deserialized into a new ArrayList
with all the objects intact.
This could be useful…yah?
To get this to work, one of the overloaded Serialize
functions in the XmlSerializer
class is used. The Serialize
function is passed an array of types to be serialized. It is easy to get all the types in the ArrayList
by iterating the list.
The problem is deserializing the list. How do you get the array of types the deserializer needs?
My Solution: Add the list of types in the ArrayList
…to the ArrayList
…before serializing it.
The solution has one drawback: Deserialization happens twice. The XmlString
is deserialized to get the array of types. Once the array of types is collected, the XmlString
is deserialized again with the array of types and the ArrayList
will be reconstituted correctly. The XmlString
could be parsed to get the array of types but it would probably be just as slow as deserializing and definitely more complicated. The list of types could be included as auxiliary information in a custom class but my self-imposed requirement was to serialize to a valid XML document which requires a single root element… so the list of types must be within the root element.
Here are the functions:
public static string SerializeArrayList(ArrayList ArrayListIn)
{
ArrayList TypesInList = new ArrayList();
foreach (Object Item in ArrayListIn)
{
if (TypesInList.Contains(Item.GetType()) == false)
TypesInList.Add(Item.GetType());
}
if (TypesInList.Contains(typeof(String)) == false)
TypesInList.Add(typeof(String));
if (TypesInList.Contains(typeof(String[])) == false)
TypesInList.Add(typeof(String[]));
String[] TypeNamesArray = new String[TypesInList.Count];
for (int i = 0; i < TypesInList.Count; i++)
TypeNamesArray[i] = ((Type)TypesInList[i]).AssemblyQualifiedName;
ArrayListIn.Add(TypeNamesArray);
Type[] Types = (Type[])TypesInList.ToArray(typeof(Type));
XmlSerializer Serializer = new XmlSerializer(typeof(ArrayList), Types);
StringWriter Writer = new StringWriter();
Serializer.Serialize(Writer, ArrayListIn);
String XMLString = Writer.ToString();
ArrayListIn.RemoveAt(ArrayListIn.Count - 1);
return XMLString;
}
public static ArrayList DeSerializeArrayList(String XmlString)
{
Type[] TempTypes = new Type[2];
TempTypes[0] = typeof(System.String);
TempTypes[1] = typeof(System.String[]);
XmlSerializer Serializer = new XmlSerializer(typeof(ArrayList), TempTypes);
ArrayList MyArrayList;
StringReader StrReader = new StringReader(XmlString);
MyArrayList = Serializer.Deserialize(StrReader) as ArrayList;
String[] StrTypes = MyArrayList[MyArrayList.Count - 1] as String[];
TempTypes = new Type[StrTypes.Length];
for (int i = 0; i < TempTypes.Length; i++)
TempTypes[i] = Type.GetType(StrTypes[i]);
Serializer = new XmlSerializer(typeof(ArrayList), TempTypes);
StrReader = new StringReader(XmlString);
MyArrayList = Serializer.Deserialize(StrReader) as ArrayList;
MyArrayList.RemoveAt(MyArrayList.Count - 1);
return MyArrayList;
}
I put the functions in a namespace called XMLUtils
(in a file called XMLUtils.cs). You can download the file here. It also includes updated versions of the two functions to serialize and deserialize single objects from an earlier post.
Note: The classes and namespaces of the objects in the ArrayList
must be available in the scope of where the serialization and deserialization takes place.
Note: I initially tried to serialize an array of Types instead of an array of Type Names. But the serializer did not like that. It didn't like it one bit.
Note: I could have created the functions as extension methods or added an extension method wrapper like this:
public static string ExSerializeArrayList(this ArrayList ArrayListIn)
{
return SerializeArrayList(ArrayListIn);
}
But, to be symmetric, an Extension method would need to be created for the String
class and that would burden the often-used String
class with a very specialized and rare usage… so it was not done.
I hope someone finds this useful.
Thanks,
Steve Wellens