15 August 2009

Using extension methods to serialize objects to XML and compress the result - and deserialize again

Elaborating upon the string extension methods I created for compressing and decompressing strings to and from a byte array, it turned out fairly easy to create another set of compress / decompress methods to serialize any type of object to and from a byte array containing compressed XML. I once again took the StringZipExtensions class and added the following methods to compress any old object to a byte array:
/// <summary>
/// XmlSerializes the object to a compressed byte array
/// using the specified encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress,
  Encoding encoding)
{
  var xmlSerializer = new XmlSerializer(objectToCompress.GetType());
  using (var stringWriter = new StringWriter())
  {
    xmlSerializer.Serialize(stringWriter, objectToCompress);
    return stringWriter.ToString().Compress(encoding);
  }
}

/// <summary>
/// XmlSerializes the object to a compressed byte array using default
/// UTF8 encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress)
{
  return Compress(objectToCompress, new UTF8Encoding());
}
Here, once again, an overload using a default UTF8 encoding and a method which uses your own encoding to do the heavy lifting. Then, the methods for decompressing, which - in all modesty - are a rather neat usage of generics, I think:
/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using the specified encoding
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="compressedObject">The compressed string.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject,
  Encoding encoding)
{
  var xmlSer = new XmlSerializer(typeof(T));
  return (T)xmlSer.Deserialize(new StringReader(
    compressedObject.DecompressToString(encoding)));
}

/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using default UTF8 encoding
/// </summary>
/// <param name="compressedObject">The compressed string.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject )
{
  return DecompressToObject<T>(compressedObject, new UTF8Encoding());
}
Then you can take something like this hugely complex ;-) object Person
public class Person
{
  public int Id { get; set; }
  public string Name { get; set; }
  public DateTime Birthday { get; set; }

  public override bool Equals(object obj)
  {
    var toCompare = obj as Person;
    if( toCompare != null )
    {
      return
        Name.Equals(toCompare.Name) &&
        Id.Equals(toCompare.Id) &&
        Birthday.Equals(toCompare.Birthday);

    }
    return base.Equals(obj);
  }
}
and serialize/compress and decompress/deserialize it like this:
[Test]
public void CompressObjectTest()
{
  var baseLineObject = new Person
  {
  Id = 99, 
  Name = "Tom", 
  Birthday = new DateTime(1969, 06, 03)
  };
  var compressed = baseLineObject.Compress();
  var testObject = compressed.DecompressToObject<Person>();
  Assert.AreEqual(testObject, baseLineObject);
}
Code downloadable here

No comments: