Serializing Data

Summary

In this blog post I’m going to talk about some tricky problems with serializing and deserializing data.  In particular, I’m going to demonstrate a problem with the BinaryFormatter used in C# to turn an object into a byte array of data.

Using the BinaryFormatter Serializer

If you are serializing an object inside your project and storing the data someplace, then deserializing the same object in your project, things will work as expected.  I’ll show an example.  First, I’ll define a generic object called AddressClass.  Which stores address information:

[Serializable]
public class AddressClass
{
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

The first thing you’ll notice is that there is a [Serializable] attribute.  This is needed in order for BinaryFormatter to serialize the object.  Next, I’ll create an instance of this object in my console application and populate with some dummy data:

// create an instance and put some dummy data into it.
var addressClass = new AddressClass
{
    Address1 = "123 Main st",
    City = "New York",
    State = "New York",
    Zip = "12345"
};

Now we’re ready to serialize the data.  In this example, I’ll just serialize this object into a byte array:

// serialize the object
using (var memoryStream = new MemoryStream())
{
    var binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(memoryStream, addressClass);
    storedData = memoryStream.ToArray();
}

There’s nothing fancy going on here.  You can even use a compressor inside the code above to compress the data before saving it someplace (like SQL or Redis or transmitting over the wire).  Now, I’m going to just deserialize the data into a new object instance:

//deserialize the object
AddressClass newObject;
using (var memoryStream = new MemoryStream())
{
    var binaryFormatter = new BinaryFormatter();
    memoryStream.Write(storedData, 0, storedData.Length);
    memoryStream.Seek(0, SeekOrigin.Begin);
    newObject = (AddressClass)binaryFormatter.Deserialize(memoryStream);
}

If you put a break-point at the end of this code as it is, you can see the newObject contains the exact same data that the addressClass instance contained.  In order to make all the code above work in one program you’ll have to include the following usings at the top:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

Deserializing in a Different Program

Here’s where the trouble starts.  Let’s say that you have two different programs.  One program serializes the data and stores it someplace (or transmits it).  Then another program will read that data and deserialize it for its own use.  To simulate this, and avoid writing a bunch of code that will distract from this blog post, I’m going to dump the serialized data as an array of integers in a text file.  Then I’m going to copy that raw text data and then use it in my second program as preset data of a byte array.  Then I’m going to copy the AddressClass code above and the deserialize code above and put it in another program.  This should deserialize the data and put it into the new object as above.  But that doesn’t happen.  Here’s the error that will occur:

Unable to find assembly 'SerializatingDataBlogPost, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. 

This error occurs on this line:

newObject = (AddressClass)binaryFormatter.Deserialize(memoryStream);

Inside the serialized data is a reference to the dll that was used to serialize the information.  If it doesn’t match, then the assumption is that BinaryFormatter will not be able to convert the serialized data back into the object defined.

If you dig around, you’ll find numerous articles on how to get around this problem.  Using a BindToType object is one method as shown here:

Unable to find assembly with BinaryFormatter.Deserialize

And it goes down a rabbit hole from there.  

Another Solution

Another solution is to serialize the data into JSON format.  The Newtonsoft serializer is very good at serializing objects into JSON.  After that, the deserialized data can be cast back into the same object inside another dll.  Use the NuGet manager to add Newtonsoft to your project.  Then use the following code to serialize your addressClass object:

// serialize the object
var serializer = new JsonSerializer();
string resultSet = JsonConvert.SerializeObject(addressClass);

This will convert your object into the following string:

{"Address1":"123 Main st","Address2":null,"City":"New York","State":"New York","Zip":"12345"}

Inside another project, you can deserialize the string above using this:

// deserialize object
AddressClass newObject;
newObject = JsonConvert.DeserializeObject<AddressClass>(resultSet);

Compressing the Data

JSON is a text format and it can take up a lot of space, so you can add a compressor to your code to reduce the amount of space that your serialized data takes up.  You can use the following methods to compress and decompress string data into byte array data:

private static byte[] Compress(string input)
{
    byte[] inputData = Encoding.ASCII.GetBytes(input);
    byte[] result;
    using (var memoryStream = new MemoryStream())
    {
        using (var zip = new GZipStream(memoryStream, CompressionMode.Compress))
        {
            zip.Write(inputData, 0, inputData.Length);
        }
        result = memoryStream.ToArray();
    }
    return result;
}

private static string Decompress(byte[] input)
{
    byte[] result;
    using (var outputMemoryStream = new MemoryStream())
    {
        using (var inputMemoryStream = new MemoryStream(input))
        {
            using (var zip = new GZipStream(inputMemoryStream, CompressionMode.Decompress))
            {
                zip.CopyTo(outputMemoryStream);
            }
        }
        result = outputMemoryStream.ToArray();
    }
    return Encoding.Default.GetString(result);
}

You’ll need the following usings:

using System.IO.Compression;
using System.IO;

Then you can pass your JSON text to the compressor like this:

// compress text
byte[] compressedResult = Compress(resultSet);

And you’ll need to decompress back into JSON before deserializing:

// decompress text
string resultSet = Decompress(compressedResult);

Where to Get the Code

As usual, you can go to my GitHub account and download the projects by clicking here.

Leave a Reply