views:

350

answers:

1

As I understand it I have to adorn a new member in a newer version of my class with the [OptionalField] Attribute when I deserialize an older version of my class that lacks this newer member.

However, the code below throws no exception while the InnerTranslator property was added after serializing the class. I check for the property to be null in the onDeserialization method (which confirms that it was not serialized),but I would have expected the code to throw an exception because of that. Is the [OptionalField] Attribute itself optional?

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;



namespace SerializationSpike
{
    class Program
    {
        static void Main(string[] args)
        {
            var listcol = new SortedList<string,string>
                              {
                                  {"Estados Unidos", "United States"},
                                  {"Canadá", "Canada"},
                                  {"España", "Spain"}
                              };
            var translator = new CountryTranslator(listcol);
            using (var file_stream=new FileStream("translator.bin",FileMode.Open))
            {
                var formatter = new BinaryFormatter();
                translator = formatter.Deserialize(file_stream) as CountryTranslator;
                file_stream.Close();
            }
            Console.ReadLine();
        }
    }

    [Serializable]
    internal class CountryTranslator:IDeserializationCallback
    {
        public int Count { get; set; }

        public CountryTranslator(SortedList<string,string> sorted_list)
        {
            this.country_list = sorted_list;
            inner_translator = new List<string> {"one", "two"};
        }
        //[OptionalField]
        private List<string> inner_translator;
        public List<string> InnerTranslator
        {
            get { return inner_translator; }
            set { inner_translator = value; }
        }

        private SortedList<string, string> country_list;

        #region Implementation of IDeserializationCallback

        public void OnDeserialization(object sender)
        {
            Debug.Assert(inner_translator == null);
            Count=country_list.Count;
        }

        #endregion
    }
}
+2  A: 

BinaryFormatter is, at the best of times, very brittle if you change things. Not least, there are huge problems with automatically implemented properties, obfuscaction, renaming, strong naming, etc.

As I recall, some of the rules about [OptionalField] changed just before it was released; the version tolerant thing didn't really work out as easily as had been planned, I expect.

My advice: if you want version tolerant serialization (i.e. you can serialize it today and deserialize it with the next version of your app), then don't use BinaryFormatter; this is (IMO) only suitable for passing data between the same version (remoting, AppDomains, etc).

For work between versions, I recommend contract-based serialization; things like XmlSerializer and DataContractSerializer (.NET 3.0), or for binary - protobuf-net or similar tools. All of these are much better at version tolerance (indeed, you don't even need to deserialize it into the same Type); plus they can be used between platforms - so you can serialize in .NET and deserialize in java/C++/etc.

Marc Gravell
Not exactly the answer I was looking for, but I found that I have switched to using the DataContractSerializer exclusively since you sugeested it in this post. So thanks after all ;-)
Dabblernl