Search code examples
c#serializationdeserializationbinary-deserialization

BinaryFormatter OptionalField with ISerializable


I create a test serializable object:

[Serializable]
public class TestObject : ISerializable
{
    public string FirstName;
    public TestObject()
    {
    }

    public TestObject(SerializationInfo info, StreamingContext context)
    {
        FirstName = info.GetString("firstName");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("firstName", FirstName);
    }
}

And then serialize it:

var test = new TestObject { FirstName = "John" };
using (var stream = new FileStream(@"c:\temp\test.dat", FileMode.Create, FileAccess.Write))
{
    var formatter = new BinaryFormatter();
    formatter.Serialize(stream, test);
}

I change the class so that it has an OptionalFied and try to Deserialize the old serialized object:

[Serializable]
public class TestObject : ISerializable
{
    public string FirstName;

    [OptionalField]
    public string SecondName;

    public TestObject()
    {
    }

    public TestObject(SerializationInfo info, StreamingContext context)
    {
        FirstName = info.GetString("firstName");
        SecondName = info.GetString("secondName");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("firstName", FirstName);
    }

When I change the constructor to read the new optional field, I get an exception thrown at "SecondName = info.GetString("secondName");":

   using (var stream = new FileStream(@"c:\temp\test1.dat", FileMode.Open, FileAccess.Read))
{
    var formatter = new BinaryFormatter();
    var myInstance = (TestObject)formatter.Deserialize(stream);
}

Is it the case that the OptionalFieldAttribute is not really supported when you implement ISerializable?


Solution

  • For the old serialzed object there would not be a secondname field, so a SerializationEntry for it wouldn't exist.

    You need to check if an entry exists for it before you access the value:

        foreach (SerializationEntry entry in info) 
        { 
            switch (entry.Name) 
            { 
                case "firstname": 
                    Firstname = (string)entry.Value; 
                break; 
                case "secondname": 
                    Secondname = (string)entry.Value;
                break; 
            }
        }