Solved

Deserializing older versions of classes that contain structs formatted with Soap

Posted on 2003-10-27
6
459 Views
Last Modified: 2008-03-17
Hi,
I previously released an application that uses the .Net in-build serialization. Ie, just adding [Serializable] to the top of each class. I would like to now make changes to some of the classes - but also have the newer version of my program be able to open files saved with the older version of my program.
It was my belief that I could use the .net [Serializable] attribute initially, and then move over to implementing ISerializable as I needed to further change specific classes. Generally this has worked well. But...

Any class serialised using just [Serializable] that also contains a field that's a struct seems to be unopenable once I implement ISerializable. Ie, if I use the new version of the program to open a file saved with the old version I get a SeralizationException: Top Object cannot be instantiated for element '_size'.

Old version of class...
[Serializable]
public class Test1
{
    string _name = null;
    Size _size = Size.Empty;
    ...
}

New version of class...
[Serializable]
public class Test1 : ISerializable
{
    string _name = null;
    Size _size = Size.Empty;
    int _newField = 0;
   
    protected Test1(SerializationInfo info, StreamingContext context)
    {
        // something... or nothing, in either case it doesn't work
    }
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // usual stuff... not necessary for this test case
    }
}

I'm using the SoapFormatter to save/open objects
static object Load()
{
    IFormatter formatter = new SoapFormatter();
    Stream s = new FileStream("c:\\test.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
    object loadedObject = formatter.Deserialize(s);
    s.Close();
    return loadedObject;
}

static void Save(object obj)
{
    Stream s = (Stream)File.Open("c:\\test.xml", FileMode.Create, FileAccess.Write);
    IFormatter b = new SoapFormatter();
    b.Serialize(s, obj);
    s.Close();
}

* If I do the whole experiment with the BinaryFormatter instead of the SoapFormatter, everything works perfectly. But the version of the product already out there is using the SoapFormatter.
Is this a bug in SoapFormatter?
* If the classes don't use structs, but just stick to classes, arrays and primitives, then I don't have any problems.

Is there anyway that I'm able to be able to open these older files, eg with some special Binder or something? Is this a bug in .net SoapFormatter maybe?
(I'd prefer to not manually process the whole XML file).

Any assistance appreciated, thanks.
0
Comment
Question by:paylett
  • 3
  • 2
6 Comments
 

Author Comment

by:paylett
Comment Utility
I've tried experimenting with the Binder, and the SoapFormatter doesn't seem to call the Binder wrt the struct.
I'll push this up to 500 points.
0
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
The Soap formatter is made to serialize objects for using the soap protocol. It's not thought to make objects persistent for a longer time! So it's not a bug that you cannot deserialize "old" objects.
Use XmlSerializer where you can control the serialization by attributes to make your objects persistent in files or database.
0
 

Author Comment

by:paylett
Comment Utility
Do you know of anyway that I would be able to read these older versions?
(I'd prefer to have the next version of my product backwards compatible if possible)
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 10

Accepted Solution

by:
ptmcomp earned 500 total points
Comment Utility
In Microsoft Developer Journal was an article from Jeffrey Richter about this serializing stuff explaining how to change the version dependency. (http://msdn.microsoft.com/msdnmag/issues/02/09/NET/default.aspx)
Else use the old version of your assembly to deserialize it. Either name the new assembly different or load the old assembly in a seperate application domain (else you cannot have both loaded at same time).
0
 

Author Comment

by:paylett
Comment Utility
The Jeffrey Richter article is by far the best I've seen so far on serialization. However, I've tried using the SurrogateSelector and I still get the same Top Object can not be instantiated for element '_size' exception.

This just seems to be a short coming in the .net serialization wrt structs. The article by Richter points out other problems with structs and serialization - perhaps they know about this too.

For the moment I'll concede that either I'm using the wrong tool for the job, or it's an oversite by MS. Am going to move to the BinaryFormatter and tell users their old files can't be opened.
0
 

Expert Comment

by:MariaSimlinger
Comment Utility
Hi,

I only signed up to experts exchange to see the solution for this problem. Nevertheless I had to find a solution on my own, I did find one, and I think I should share it with you.

I assume you tried to use the Binder class with the struct, and that didn't work.

Try to rename the old version of your class to something like Test1Old and inherit (a new) Test1 from this old version. Test1Old might have to be modified a little bit. The new version of Test1 implements ISerializable.

Set the AssemblyVersion from 1.0.0.0 to 2.0.0.0 (assuming 1.0.0.0 is your current version) and implement a Binder Class. But check for class Test1Old in the binder (see example). Register the Binder with the SoapFormatter (of course). It *will* be called during deserialization if you implement it this way!

To avoid casting problems in your Front-End implement IObjectReference on Test1Old.

// New version of class
[Serializable]
public class Test1 : Test1Old, ISerializable
{
   // only new properties go here
    ...
}

//Old version of class renamed, but IObjectReference returns new Version...
[Serializable]
public class Test1Old : IObjectReference
{
    string _name = null;
    Size _size = Size.Empty;
    int _newField = 0;

    // if necessary
    public Test1V2() : base()
   {
   }

    // GetRealObject is called after this object is deserialized.
    public Object GetRealObject(StreamingContext context)
    {
         if (this.GetType() != typeof(Test1Old))
               return this;
        Test1 t = new Test1();
        // set properties for t from *this*
       ...
    }
}

Check AssemblyVersion in Binder (thank’ to Jeffrey Richter for his great arcticle):

sealed class Ver1ToVer2DeserializationBinder : SerializationBinder {
   public override Type BindToType(
      string assemblyName, string typeName) {

      Type typeToDeserialize = null;

      // For each assemblyName/typeName that you wish to deserialize
      // to a different type, set typeToCreate to the desired type
      String assemVer1 = "MyAssem, Version=1.0.0.0, " +
         "Culture=neutral, PublicKeyToken=b77a5c561934e089";
      String typeVer1 = "Test1";

      if (assemblyName == assemVer1 && typeName == typeVer1) {
         assemblyName = assemblyName.Replace("1.0.0.0", "2.0.0.0");
      }

      // To return the type, do this:
      typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
         "Test1Old", assemblyName));

      return typeToDeserialize;
   }
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Introduction This article series is supposed to shed some light on the use of IDisposable and objects that inherit from it. In essence, a more apt title for this article would be: using (IDisposable) {}. I’m just not sure how many people would ge…
This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now