Thursday 4 October 2012

A Quick Note On Binary Serialization

So was playing about with Binary serialization, and think I have a nice base class you might want to use if you ever want serialize your classes. This is the base class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace SerializationXNA
{
    [Serializable]
    public class SerializableBase<T> : ISerializable
    {
        /// <summary>
        /// ctor
        /// </summary>
        public SerializableBase() { }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="info">Data to be serialized</param>
        /// <param name="context">Streaming context</param>  
        public SerializableBase(SerializationInfo info, StreamingContext ctxt)
            : this()
        {
            PropertyInfo[] properties = this.GetType().GetProperties();

            int propertyCount = properties.Length;

            for (int p = 0; p < propertyCount; p++)
                properties[p].SetValue(this, info.GetValue(properties[p].Name, properties[p].PropertyType), null);
        }

        /// <summary>
        /// Method to serialize the object
        /// </summary>
        /// <returns></returns>
        public static byte[] Serialize(T objectInsatnce)
        {
            BinaryFormatter formatter = new BinaryFormatter();

            MemoryStream memStream = new MemoryStream();
            formatter.Serialize(memStream, objectInsatnce);

            byte[] buffer = memStream.ToArray();
            memStream.Close();

            

            return buffer;

        }

        /// <summary>
        /// Method to deserialize the object from a byte array
        /// </summary>
        /// <param name="buffer">byte array holding the serialized object</param>
        /// <returns>Deserialized instance of the object</returns>
        public static T Deserialize(byte[] buffer)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            MemoryStream memStream = new MemoryStream(buffer);

            T retVal = (T)formatter.Deserialize(memStream);
            
            memStream.Close();

            return retVal;
        }

        /// <summary>
        /// Needed to do binary serailization of this object
        /// </summary>
        /// <param name="info">Data to be serialized</param>
        /// <param name="context">Streaming context</param>  
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            PropertyInfo[] properties = this.GetType().GetProperties();

            int propertyCount = properties.Length;

            for (int p = 0; p < propertyCount; p++)
                info.AddValue(properties[p].Name, properties[p].GetValue(this, null));
        }
    }
}

Not a great deal in there other than it has to have the Serializable class attribute and implement the ISerializable interface and uses a generic in it’s definition, there are two constructors, the second needed for de-serialization. We then have Serialize and Deserialize statics, both methods do exactly what they say and then finally we implement the ISerializable method GetObjectData.

So pretty simple stuff, this is how you can then derive from this to then use all the lovely serialization stuff in the base class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace SerializationXNA
{
    [Serializable]
    public class SerializableClass : SerializableBase<SerializableClass>
    {
        public bool IsDirty { get; set; }
        public string Name { get; set; }
        public Dictionary<string, object> ObjDictionary { get; set; }
        public List<string> StringList { get; set; }

        public SerializableClass()
        {
            IsDirty = false;
            Name = string.Empty;
            ObjDictionary = new Dictionary<string, object>();
            StringList = new List<string>();
        }

        public SerializableClass(SerializationInfo info, StreamingContext ctxt)
            : base(info, ctxt)
        { }
    }
}

As you can see a pretty simple class, has 4 properties and a couple of constructors, but as we have derived from SerializableBase class we get all the serializable goodness, so this is how you could implement it

            SerializableClass classToSerialize = new SerializableClass();

            classToSerialize.IsDirty = true;
            classToSerialize.Name = "Date To serialize";
            classToSerialize.ObjDictionary.Add("One", "One");
            classToSerialize.ObjDictionary.Add("Two", 3);
            classToSerialize.StringList.Add("Hello");
            classToSerialize.StringList.Add("World");

            byte[] buffer = SerializableClass.Serialize(classToSerialize);

            SerializableClass deserializedClass = SerializableClass.Deserialize(buffer);

            deserializedClass.Name = "Deserialized data";

The great thing about this is that you can go and add as many properties as you like and you wont have to change any of the serialization code as it will automatically pick up the new properties :)

EDIT

Just altered the base class so it’s now WP7 friendly, I guess you could go 100% with the DataContractSerializer though…

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;
#if WINDOWS
using System.Runtime.Serialization.Formatters.Binary;
#endif
using System.IO;

namespace SerializationXNA
{
#if WINDOWS
    [Serializable]
#endif
    public class SerializableBase<T>
#if WINDOWS
        : ISerializable
#endif
    {
        /// <summary>
        /// ctor
        /// </summary>
        public SerializableBase() { }

#if WINDOWS
        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="info">Data to be serialized</param>
        /// <param name="context">Streaming context</param>  
        public SerializableBase(SerializationInfo info, StreamingContext ctxt)
            : this()
        {
            PropertyInfo[] properties = this.GetType().GetProperties();

            int propertyCount = properties.Length;

            for (int p = 0; p < propertyCount; p++)
                properties[p].SetValue(this, info.GetValue(properties[p].Name, properties[p].PropertyType), null);
        }
#endif
        /// <summary>
        /// Method to serialize the object
        /// </summary>
        /// <returns></returns>
        public static byte[] Serialize(T objectInsatnce)
        {
#if WINDOWS
            BinaryFormatter formatter = new BinaryFormatter();
#else
            DataContractSerializer formatter = new DataContractSerializer(typeof(T));
#endif

            MemoryStream memStream = new MemoryStream();
#if WINDOWS
            formatter.Serialize(memStream, objectInsatnce);
#else
            formatter.WriteObject(memStream, objectInsatnce);
#endif
            byte[] buffer = memStream.ToArray();
            memStream.Close();

            

            return buffer;

        }

        /// <summary>
        /// Method to deserialize the object from a byte array
        /// </summary>
        /// <param name="buffer">byte array holding the serialized object</param>
        /// <returns>Deserialized instance of the object</returns>
        public static T Deserialize(byte[] buffer)
        {
#if WINDOWS
            BinaryFormatter formatter = new BinaryFormatter();
#else
            DataContractSerializer fomratter = new DataContractSerializer(typeof(T));
#endif
            MemoryStream memStream = new MemoryStream(buffer);

#if WINDOWS
            T retVal = (T)formatter.Deserialize(memStream);
#else
            T retVal = (T)fomratter.ReadObject(memStream);

#endif
            memStream.Close();

            return retVal;
        }
#if WINDOWS
        /// <summary>
        /// Needed to do binary serailization of this object
        /// </summary>
        /// <param name="info">Data to be serialized</param>
        /// <param name="context">Streaming context</param>  
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            PropertyInfo[] properties = this.GetType().GetProperties();

            int propertyCount = properties.Length;

            for (int p = 0; p < propertyCount; p++)
                info.AddValue(properties[p].Name, properties[p].GetValue(this, null));
        }
#endif
    }
}

No comments:

Post a Comment