Serialization when extending a class whose GetObjectData method is not marked virtual

6.7k views Asked by At

I'm trying to extend a framework. One of the classes I am extending is serialized. The base class' GetObjectData() method is not marked virtual so I can't override it.

Now if object gets serialized when it's referenced as a base class it's not polymorphic so only the base class' GetObjectData is called.

Is there any way around this without modifying the base class' GetObjectData to mark it as virtual?

[Edit] I extended the class and added an attribute that I want to serialize. Simple example of the problem below

[Serializable]
public class ParentClass : ISerializable 
{

    public float m_parent = 1.73f;

    public ParentClass() 
    { }

    public ParentClass(SerializationInfo info, StreamingContext context)
    {
        Debug.Log("Loading parent");
        m_parent = (float)info.GetValue("m_parent", typeof(float));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        Debug.Log("Saving parent");
        info.AddValue("m_parent", m_parent, typeof(float));
    }
}


[Serializable]
public class ChildClass : ParentClass
{
    public int m_child = 73;

    public ChildClass()
    { }

    public ChildClass(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        Debug.Log("Loading child");
        m_child = (int)info.GetValue("m_child", typeof(int));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        Debug.Log("Saving child");
        info.AddValue("m_child", m_child, typeof(int));
        base.GetObjectData(info, context);
    }
}

void Save()
{
    Debug.Log("Starting save");
    ParentClass aParent = new ChildClass() as ParentClass;
    using (Stream stream = File.Open(fileName, FileMode.Create))
    {
        BinaryFormatter bFormatter = new BinaryFormatter();
        bFormatter.Serialize(stream, aParent);
    }
    Debug.Log("Save complete");
}

void Load()
{
    Debug.Log("Starting load");
    ChildClass aChild;
    using (Stream stream = File.Open(fileName, FileMode.Open))
    {
        BinaryFormatter bFormatter = new BinaryFormatter();
        aChild = bFormatter.Deserialize(stream) as ChildClass;
    }
    Debug.Log("Load complete" + aChild.m_child);
}

Doing a save/load yields the following error:

SerializationException: No element named m_child could be found.

3

There are 3 answers

0
dbc On BEST ANSWER

You need to do two things:

  1. Mark ChildClass explicitly as being ISerializable.

  2. Declare a GetObjectData with the new modifier in the child class as Will suggests,

    or

    Implement the interface explicitly in the child class.

For instance:

[Serializable]
public class ChildClass : ParentClass, ISerializable
{
    public int m_child = 73;

    public ChildClass()
    { }

    public ChildClass(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        Debug.WriteLine("Loading child");
        m_child = (int)info.GetValue("m_child", typeof(int));
    }

    public new void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        Debug.WriteLine("Saving child");
        info.AddValue("m_child", m_child, typeof(int));
        base.GetObjectData(info, context);
    }
}

Since BinaryFormatter.Serialize(Stream, Object) is non-generic, the most derived interface implementation will be discovered and used.

For details of why this works, see the c# language specification 17.6.7 Interface re-implementation:

A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list.

1
AudioBubble On

You can use the new modifier to override the base class' implementation.


If it's referenced by the base class by the serializer, this won't help. It would have to be referenced as the child type for the new override to work.

In this case, your only hopes would be to 1) replace serialization logic so you can control it, 2) use a proxy class for serialization purposes that is mapped back and forth to the framework class, 3) fork the framework to fix its limitations or 4) magic

4
Ron Beyer On

Without knowing exactly what you need GetObjectData for, you can use some custom methods to manipulate your object during serialization/deserialization:

[Serializable]
public MySerializableClass : MyUnforgivingBaseClass
{
    [OnSerializing]
    public void OnSerializing(StreamingContext context)
    {
         //You can modify the object before serialization here
    }

    [OnDeserializing]
    public void OnDeserializing(StreamingContext context)
    {
        //You can modify the object during deserialization here
    }
}

See https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializingattribute(v=vs.110).aspx and https://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializingattribute%28v=vs.110%29.aspx