binary serialization, adding a new field to class - will it work?

2.3k views Asked by At

I have a client and a server application which communicate over .NET 2.0 Remoting using binary serialization.

A small change has been made to one of the data transfer object's interface and the implementing class, well, an array of strings field was added.

If I to redeploy a new version of server application, will my old clients continue to work?

I would think they would, since nothing has been deleted from interface and direct implementation, but I am not sure.

It probably boils down to another question - is a binary deserialiser "clever enough" to handle the situation like this by initializing a fields it fails to find data in the input binary stream to null, or is it going to break and to throw the exception?

2

There are 2 answers

0
Grant Palin On BEST ANSWER

You can add an attribute to the new property: OptionalField. Without the attribute, the deserializer will not be able to convert serialized data back to the updated definition. The attribute is to ensure the deserializer can handle the "missing" data gracefully.

If you want to set a default value for the new property, in the case that no appropriate data for it is deserialized, implement the IDeserializationCallback interface, and set the default value, if any, in the resulting method.

0
t0mm13b On

More than likely it is going to throw an exception, you could always implement your own Serializer by inheriting from ISerializable and implement the versioning by using your own methods of GetObjectData...this will give you a tighter degree of control over the data to be serialized...Here's an example

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

public class MyFooBar : ISerializable{
    private float _fVersion = 1.0;
    public MyFooBar(SerializationInfo info, StreamingContext context) {
         this._fVersion = info.GetSingle("FooBarVersionID");
         if (this._fVersion == 1.0F) bOk = this.HandleVersionOnePtZero(info, context);
         if (!bOk) throw new SerializationException(string.Format("MyFooBar: Could not handle this version {0}.", this._fVersion.ToString()));
    }
    [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)]
   public void GetObjectData(SerializationInfo info, StreamingContext context) {
       info.AddValue("FooBarVersionID", this._fVersion);
       if (this._fVersion == 1.0F) {
          // Bool's...
          info.AddValue("FooBarBool", FooBarBool);
          // etc... for Version 1.0
       }
       if (this._fVersion == 1.1F){
          // etc... for Version 1.0
       }
   }
}

And use MyFooBar in this context when serializing/deserializing as shown below

public bool Deserialize(string sFileName) {
            bool bSuccessful = false;
            //
            if (!System.IO.File.Exists(sFileName)) return false;
            fuBar = new MyFooBar();
            //
            try {
                using (FileStream fStream = new FileStream(sFileName, FileMode.Open)) {
                    try {
                        BinaryFormatter bf = new BinaryFormatter();
                        fuBar = (MyFooBar)bf.Deserialize(fStream);
                        bSuccessful = true;
                    } catch (System.Runtime.Serialization.SerializationException sEx) {
System.Diagnostics.Debug.WriteLine(string.Format("SERIALIZATION EXCEPTION> DETAILS ARE {0}", sEx.ToString()));
                        bSuccessful = false;
                    }
                }
            } catch (System.IO.IOException ioEx) {
                System.Diagnostics.Debug.WriteLine(string.Format("IO EXCEPTION> DETAILS ARE {0}", ioEx.ToString()));
                bSuccessful = false;
            }
            return (bSuccessful == true);
        }

There is a more neater way to do this in 2.0+ upwards, but I prefer this way.