Contents tagged with Serialize

  • Serializing ArrayLists of Disparate Objects to XML Strings

    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);    // Serialize  
     
    MyArrayList = XMLUtils.DeSerializeArrayList(MyXml);         // Deserialize
     
    Response.Write("----- After deserialization ------ <br />");
    foreach (Object O in MyArrayList)
        Response.Write(O.ToString() + "<br />");
     
    // Output:
    //        ----- Before serialization ------ 
    //        Sue
    //        1/5/2010 12:50:15 PM
    //        Bob
    //        1234
    //        ----- After deserialization ------ 
    //        Sue
    //        1/5/2010 12:50:15 PM
    //        Bob
    //        1234
     
       

    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:

    /// ---- SerializeArrayList ----------------
    /// <summary>
    /// Serialize an ArrayList to an XML string
    /// </summary>
    /// <param name="ArrayListIn">An ArrayList</param>
    /// <returns>XML String</returns>
     
    public static string SerializeArrayList(ArrayList ArrayListIn)
    {
        // get a list of all the types in the ArrayList to serialize
        // avoid duplicates
        ArrayList TypesInList = new ArrayList();
     
        foreach (Object Item in ArrayListIn)
        {
            if (TypesInList.Contains(Item.GetType()) == false)
                TypesInList.Add(Item.GetType());
        }
     
        // we're going to add a string array, so add these two types
        if (TypesInList.Contains(typeof(String)) == false)
            TypesInList.Add(typeof(String));
        if (TypesInList.Contains(typeof(String[])) == false)
            TypesInList.Add(typeof(String[]));
     
        // Convert the list of types to a string array of type names
        // so we can add it to the ArrayList
        String[] TypeNamesArray = new String[TypesInList.Count];
     
        for (int i = 0; i < TypesInList.Count; i++)        
            TypeNamesArray[i] = ((Type)TypesInList[i]).AssemblyQualifiedName;
     
        // add the array of type names to the ArrayList
        ArrayListIn.Add(TypeNamesArray);
     
        // The Serialize function require an array of type objects
        Type[] Types = (Type[])TypesInList.ToArray(typeof(Type));
     
        // create the serializer, give it the ArrayList type and 
        // the array of types in the ArrayList
        XmlSerializer Serializer = new XmlSerializer(typeof(ArrayList), Types);
     
        // create a stream for the Serializer output
        StringWriter Writer = new StringWriter();
     
        // Do the Serialization
        Serializer.Serialize(Writer, ArrayListIn);
     
        String XMLString = Writer.ToString();
     
        // remove the string array of types we added.
        // we don't want to affect the original list.
        ArrayListIn.RemoveAt(ArrayListIn.Count - 1);
        return XMLString;
    }
     
    /// ---- DeSerializeArrayList --------------------------------------
    /// <summary>
    /// Deserialize an XML string to an ArrayList that was
    /// serialized with the SerializeArrayList function.
    /// </summary>
    /// <param name="XmlString">XML String to deserialize</param>
    /// <returns>A populated ArrayList</returns>
     
    public static ArrayList DeSerializeArrayList(String XmlString)
    {
        // extract the array of type names
        // it's the last node of the ArrayList
        Type[] TempTypes = new Type[2];
     
        TempTypes[0] = typeof(System.String);
        TempTypes[1] = typeof(System.String[]);
        XmlSerializer Serializer = new XmlSerializer(typeof(ArrayList), TempTypes);
     
        ArrayList MyArrayList;
     
        // create a stream for the DeSerializer input
        StringReader StrReader = new StringReader(XmlString);
     
        // first deserialization (we'll do this again)
        MyArrayList = Serializer.Deserialize(StrReader) as ArrayList;
     
        // get the array of type names (last node in ArrayList)
        String[] StrTypes = MyArrayList[MyArrayList.Count - 1] as String[];
     
        // convert it to an array of type objects
        TempTypes = new Type[StrTypes.Length];
        for (int i = 0; i < TempTypes.Length; i++)
            TempTypes[i] = Type.GetType(StrTypes[i]);
     
        // create the serializer, this time with a complete array
        // of the types contained in it.
        Serializer = new XmlSerializer(typeof(ArrayList), TempTypes);
     
        // create a stream for the DeSerializer output
        StrReader = new StringReader(XmlString);
     
        // Deserialize
        MyArrayList = Serializer.Deserialize(StrReader) as ArrayList;
     
        // get rid of the array of type names, we put it
        // there and it's not part of the orginal 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

  • Serializing and Deserializing Objects…to and from…XML

    Over on the Asp.Net forums I recently had the opportunity* to help a few lost souls by showing them how to serialize objects to XML and deserialize the XML back into objects. Since the question has come up more than once, I decided to BLOG it so I could refer similar questions in the future to this post.

    *I use the word opportunity because by helping others I am forced to think hard about the technology and to think even harder about how to communicate the technology. It makes me better at what I do. All right then, enough after-school-special-feel-good-about-yourself-I'm-ok-you're-ok fluffy nonsense… on with the code:

    Here is a simple class I'm going to work with. It has both properties and fields:

    public class MyClass
    {
        // old school property
        private int _Age;  
        public int Age  
        {
            get { return _Age; }
            set { _Age = value; }
        }
     
        // new school property
        public bool Citizen { get; set; }
     
        // there's nothing wrong with using fields
        public string Name;  
    }

    Here are the two functions to Serialize and Deserialize an object:

    /// ---- SerializeAnObject -----------------------------
    /// <summary>
    /// Serializes an object to an XML string
    /// </summary>
    /// <param name="AnObject">The Object to serialize</param>
    /// <returns>XML string</returns>
     
    public static string SerializeAnObject(object AnObject)
    {
        XmlSerializer Xml_Serializer = new XmlSerializer(AnObject.GetType());
        StringWriter Writer = new StringWriter();      
     
        Xml_Serializer.Serialize(Writer, AnObject);
        return Writer.ToString();
    }
     
     
    /// ---- DeSerializeAnObject ------------------------------
    /// <summary>
    /// DeSerialize an object
    /// </summary>
    /// <param name="XmlOfAnObject">The XML string</param>
    /// <param name="ObjectType">The type of object</param>
    /// <returns>A deserialized object...must be cast to correct type</returns>
     
    public static Object DeSerializeAnObject(string XmlOfAnObject, Type ObjectType)
    {       
        StringReader StrReader = new StringReader(XmlOfAnObject);
        XmlSerializer Xml_Serializer = new XmlSerializer(ObjectType);
        XmlTextReader XmlReader = new XmlTextReader(StrReader);
        try
        {
            Object AnObject = Xml_Serializer.Deserialize(XmlReader);
            return AnObject;
        }
        finally
        {
            XmlReader.Close();
            StrReader.Close();
        }
    }

    Here is some sample code showing how to use the functions.

    Note: I keep these functions (and other functions) in a class I call MiscUtilities. You will have to modify the code…depending on where you place the functions.

    protected void Button1_Click(object sender, EventArgs e)
    {
        // create and initialize an object
        MyClass Test = new MyClass();
     
        Test.Age = 18;
        Test.Name = "Rocky Balboa";
        Test.Citizen = true;
     
        //  Serialize it
        String XML;
     
        XML = MiscUtilities.SerializeAnObject(Test);
     
        // Deserialize it
        MyClass Test2;
     
        Test2 = MiscUtilities.DeSerializeAnObject(XML, typeof(MyClass)) as MyClass;
     
        // TODO:  Get a cup of coffee and bask in the glory of rock solid code.
    }

    Here is what the XML string looks like (after formatting):

    <?xml version="1.0"?>
    <MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Name>Rocky Balboa</Name>
      <Age>18</Age>
      <Citizen>true</Citizen>
    </MyClass>

    There are limitations: XmlSerializer does not serialize private fields, methods, indexers or read-only fields.

    Once you have the XML string, you can email it, store it in a database, save it to disk, or…print a copy of it and have your mom tape it to the refrigerator next to the picture of a turkey you made in second grade by tracing around your hand with a Crayola crayon.

    I hope someone finds this useful.