Solved

Deserializing older versions of classes that contain structs formatted with Soap

Posted on 2003-10-27
6
480 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
ID: 9639342
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
ID: 9639929
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
ID: 9653898
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
VMware Disaster Recovery and Data Protection

In this expert guide, you’ll learn about the components of a Modern Data Center. You will use cases for the value-added capabilities of Veeam®, including combining backup and replication for VMware disaster recovery and using replication for data center migration.

 
LVL 10

Accepted Solution

by:
ptmcomp earned 500 total points
ID: 9663545
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
ID: 9667916
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
ID: 12541819
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

Use Case: Protecting a Hybrid Cloud Infrastructure

Microsoft Azure is rapidly becoming the norm in dynamic IT environments. This document describes the challenges that organizations face when protecting data in a hybrid cloud IT environment and presents a use case to demonstrate how Acronis Backup protects all data.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Article by: Ivo
C# And Nullable Types Since 2.0 C# has Nullable(T) Generic Structure. The idea behind is to allow value type objects to have null values just like reference types have. This concerns scenarios where not all data sources have values (like a databa…
This article introduced a TextBox that supports transparent background.   Introduction TextBox is the most widely used control component in GUI design. Most GUI controls do not support transparent background and more or less do not have the…
In a recent question (https://www.experts-exchange.com/questions/28997919/Pagination-in-Adobe-Acrobat.html) here at Experts Exchange, a member asked how to add page numbers to a PDF file using Adobe Acrobat XI Pro. This short video Micro Tutorial sh…
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

772 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