binary serialization incompatibility between 1.0 and 1.1

  • Thread starter Thread starter John
  • Start date Start date
J

John

I serilze a array of classes in an application complied
with 1.0 using custom binary serilization.

Following is the deserilizing code:

MyClass[] _c = (MyClass[])info.GetValue("MyClass", typeof
(MyClass[]));

In 1.0, everything works fine. The MyClass' constructor
got invoked right after the above line.

But when I debug it under 1.1, _c came out with correct
dimension but all array elements are invalid until the
whole deserilizing is finished, which is too late for me,
since I have to do something immidiately after this line
of code, and the action will depend on the content in _c,
I am pretty much hosted.

Are there any work around?
Thanks
John
 
Hello John,

Thanks for your post. As I understand, the problem you are facing that the
you .NET 1.1 application does not deserialize properly the binary
serialized by 1.0 version of the application. Please correct me if there is
any misunderstanding. I think more information is needed before moving
forward:

Does it work as expected if you use the 1.1 version application to
serialize/deserialize the classes?
Could you please a sample project which is able to reproduce the problem?
It will be most helpful to pinpoint the problem and resolution.

Have a nice day!

Regards,

HuangTM
Microsoft Online Partner Support
MCSE/MCSD

Get Secure! -- www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Hi Tim,

Thanks for your response.

I was trying to send files via email but it was kicked
back.

Is this the correct email : (e-mail address removed)?

If you don't mind, would you send me your email address so
that I can send you the whole project?

Thanks

John
 
Tim,

Here is the sample code:

using System;
using System.Collections;
using System.Data;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleApplication1
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
/// <summary>
/// The main entry point for the
application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start
application here
//
Ser();
DeSer();
}


static void Ser()
{
P _p = new P();
string FileName
= "c:\\temp\\test.bin";

Stream stream = File.Open
(FileName, FileMode.Create);
BinaryFormatter bformatter = new
BinaryFormatter();
bformatter.Serialize(stream, _p);
stream.Close();
}
static void DeSer()
{
string FileName
= "c:\\temp\\test.bin";
Stream stream = File.Open
(FileName, FileMode.Open,FileAccess.Read);
BinaryFormatter bformatter = new
BinaryFormatter();
bformatter.Binder = new MyBinder
();
P _p = (P)bformatter.Deserialize
(stream);

stream.Close();
}

}
[Serializable]
public class P: CollectionBase,ISerializable
{
public P()
{
List.Add(new PText("t1"));
List.Add(new PText("t2"));
}
public P(SerializationInfo info,
StreamingContext context)
{

PText[] t = (PText[])info.GetValue
("text", typeof(PText[]));
foreach (PText ptext in t)
{
List.Add(ptext);
}
}
public void GetObjectData
(SerializationInfo info,StreamingContext context)
{
PText[] t = new PText[List.Count];
List.CopyTo(t,0);
info.AddValue("text",t);

}

}

[Serializable]
public class PText : ISerializable
{
string _t = "test1";
public PText()
{
}
public PText(string str)
{
_t = str;
}
public PText(SerializationInfo info,
StreamingContext context)
{
_t = (string)info.GetValue("text",
typeof(string));
}
public void GetObjectData
(SerializationInfo info,StreamingContext context)
{
info.AddValue("text",_t);
}
public string Text
{
get
{
return _t;
}
set
{
_t = value;
}

}

}
class MyBinder : SerializationBinder
{
public MyBinder(){;}
public override Type BindToType(string
assemblyName,string typeName)
{
return System.Type.GetType
(typeName);
}
}
}
 
John said:
I serilze a array of classes in an application complied
with 1.0 using custom binary serilization.

Following is the deserilizing code:

MyClass[] _c = (MyClass[])info.GetValue("MyClass", typeof
(MyClass[]));

In 1.0, everything works fine. The MyClass' constructor
got invoked right after the above line.

But when I debug it under 1.1, _c came out with correct
dimension but all array elements are invalid until the
whole deserilizing is finished, which is too late for me,
since I have to do something immidiately after this line
of code, and the action will depend on the content in _c,
I am pretty much hosted.

Using the sample application you posted in another message on this
thread, I see the problem you're describing, but I'm running on
Framework 1.0 SP2, so it's not a problem new with 1.1.

I looked at the change lists for 1.0 SP1 and SP2 and the 'breaking
changes' lists for 1.1, but didn't see anything there that seemed to
have anything to do with this.

It looks like the framework calls the ISerializable specified
constructor - ctor( SerializationInfo info, StreamingContext context) -
for the array, which does the bare minimum deserialization for the
array, then after that constructor returns it calls the ISerializable
specified constructor for the elements of the array.

It sounds like the behavior you desire is for the call to
info.GetValue() in the array constructor to call the element's
constructors 'recursively' (I use the term - recursively - here somewhat
reluctantly, since we're not really recursing, but I hope the idea of
what I'm describing is clear).

I think it might be interesting to hear what you need to do immediately
after the line you cite above - maybe there's another option.
 
Hi John,

I received your email with sample project. I am checking this issue and
will update you with my information.

Have a nice day!

Regards,

HuangTM
Microsoft Online Partner Support
MCSE/MCSD

Get Secure! -- www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
As shown in the sample code. The class is inherited from
CollectionBase. Since I found the collectionBase cannot
deserilize its List correctly. I found a work around using
array to ser/deserialize the elements. After I get the
array out using .GetValue, I can put them in the List.

But now, the class' constructor will not be invoked
in .GetValue, when I call List.Add(), List complains I am
trying to add in null value.

Because the old version was ship a year ago, I have to
maintain the backward compatability.

I found a work around, though not a pretty one. I keep a
class array to hold the deserilzing result and keep the
List empty. Filling the List from the class array will be
trigger by any attemping to access the List operation such
as .Count, [index] and so on.

Any suggestions?

Thanks
John
-----Original Message-----
John said:
I serilze a array of classes in an application complied
with 1.0 using custom binary serilization.

Following is the deserilizing code:

MyClass[] _c = (MyClass[])info.GetValue("MyClass", typeof
(MyClass[]));

In 1.0, everything works fine. The MyClass' constructor
got invoked right after the above line.

But when I debug it under 1.1, _c came out with correct
dimension but all array elements are invalid until the
whole deserilizing is finished, which is too late for me,
since I have to do something immidiately after this line
of code, and the action will depend on the content in _c,
I am pretty much hosted.

Using the sample application you posted in another message on this
thread, I see the problem you're describing, but I'm running on
Framework 1.0 SP2, so it's not a problem new with 1.1.

I looked at the change lists for 1.0 SP1 and SP2 and the 'breaking
changes' lists for 1.1, but didn't see anything there that seemed to
have anything to do with this.

It looks like the framework calls the ISerializable specified
constructor - ctor( SerializationInfo info, StreamingContext context) -
for the array, which does the bare minimum deserialization for the
array, then after that constructor returns it calls the ISerializable
specified constructor for the elements of the array.

It sounds like the behavior you desire is for the call to
info.GetValue() in the array constructor to call the element's
constructors 'recursively' (I use the term - recursively - here somewhat
reluctantly, since we're not really recursing, but I hope the idea of
what I'm describing is clear).

I think it might be interesting to hear what you need to do immediately
after the line you cite above - maybe there's another option.

--
mikeb


.
 
John said:
As shown in the sample code. The class is inherited from
CollectionBase. Since I found the collectionBase cannot
deserilize its List correctly. I found a work around using
array to ser/deserialize the elements. After I get the
array out using .GetValue, I can put them in the List.

But now, the class' constructor will not be invoked
in .GetValue, when I call List.Add(), List complains I am
trying to add in null value.

Because the old version was ship a year ago, I have to
maintain the backward compatability.

I found a work around, though not a pretty one. I keep a
class array to hold the deserilzing result and keep the
List empty. Filling the List from the class array will be
trigger by any attemping to access the List operation such
as .Count, [index] and so on.

Any suggestions?

Yes - you want to also inherit from IDeserializationCallback. The
method defined by this interface will be called once all of the objects
have been deserialized.

I'd also suggest putting the reference to the deserialized array in a
private instance member rather than a class array (I assume you mean
static?). When OnDeserialization() is called, you can walk the array
and call List.Add() with those objects - they are fully deserialized at
that point.

See a modified version of your sample at the end of this post for details.

I was confused at first when running your sample in framework 1.0,
because the PText objects were not deserialized when you added them back
into the list (the Text property was always the empty string). but in
framework 1.1, the Text property is null instead of the empty string, so
List.Add() complains.

Thanks
John

//====== Sample implementing IDeserializationCallback ====

using System;
using System.Collections;
using System.Data;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinarySerialization {
/// <summary>
/// Summary description for Class1.
/// </summary>
class Test {
static private MemoryStream mstream = new MemoryStream();

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args) {
//
// TODO: Add code to start application here
//
Ser();
DeSer();
}


static void Ser() {
P _p = new P();

BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Serialize(mstream, _p);
mstream.Seek( 0, SeekOrigin.Begin);
}

static void DeSer() {
mstream.Seek( 0, SeekOrigin.Begin);

BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Binder = new MyBinder();
P _p = (P)bformatter.Deserialize( mstream);

mstream.Seek( 0, SeekOrigin.Begin);

// show that the deserialization worked
foreach (PText ptext in _p) {
Console.WriteLine( "Text is \"{0}\"", ptext.Text);
}
}

}
[Serializable]
public class P: CollectionBase,ISerializable,
IDeserializationCallback {

public P() {
List.Add(new PText("t1"));
List.Add(new PText("t2"));
}


private PText[] t = null;
public P(SerializationInfo info, StreamingContext context) {
// deserialize the array into the private array reference.
// once OnDeserialization() is called, we can walk through the
// array to rebuild the Llst
t = (PText[])info.GetValue("text", typeof(PText[]));
}

public void GetObjectData
(SerializationInfo info,StreamingContext context) {
PText[] t = new PText[List.Count];
List.CopyTo(t,0);
info.AddValue("text",t);
}

void IDeserializationCallback.OnDeserialization(Object sender) {
// all objects have been deserialized, now we can walk
// through the array, and add the itmes back ino the list
foreach (PText ptext in t) {
List.Add(ptext);
}

t = null; // clear the array reference so the array
// can be garbage collected
}

}

[Serializable]
public class PText : ISerializable {
string _t = "test1";
public PText() {
}

public PText(string str) {
_t = str;
}

public PText(SerializationInfo info, StreamingContext context) {
_t = (string)info.GetValue("text", typeof(string));
}

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

public string Text {
get {
return _t;
}
set {
_t = value;
}
}
}


class MyBinder : SerializationBinder {
public MyBinder(){;}
public override Type BindToType(string assemblyName,
string typeName) {
return System.Type.GetType(typeName);
}
}
}

//=========================================================
 
Back
Top