Surrogate type conversion exception created in ISerializable.GetObjectData when an object is a field in the Serializable class

I have a value object class (UserData) that is marked as Serializable and contains another class (StringType) that is marked as Serializable and implements ISerializable so that it can return singleton instances. The StringType class serializes and deserializes its own on it, but when it is used as a property for another object that is marked as Serializable, I get an exception for converting from the deserialization helper class to singleton.

An object of type "Spring2.Core.Test.Serialization.StringType_DEFAULT" cannot be converted to a type of "Spring2.Core.Test.Serialization.StringType".

I am using BinaryFormatter and should use this to save this UserData object in an ASP.NET session using SQL Server repository.

Here's a very stripped down version of the StringType class, as well as some tests that show that serialization / deserialization works for StringType on their own, but not when this field is in UserData.

StringType:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace Spring2.Core.Test.Serialization {

    public enum TypeState : short {
    DEFAULT,
    VALID,
    UNSET
    }

    [Serializable]
    public struct StringType : ISerializable {
    private string myValue;
    private TypeState myState;

    public static readonly StringType DEFAULT = new StringType(TypeState.DEFAULT);
    public static readonly StringType UNSET = new StringType(TypeState.UNSET);

    private StringType(TypeState state) {
        myState = state;
        myValue = "";
    }

    public StringType(String s) {
        myValue = s;
        myState = TypeState.VALID;
    }

    public bool IsValid {
        get { return myState == TypeState.VALID; }
    }

    public bool IsDefault {
        get { return myState == TypeState.DEFAULT; }
    }

    public bool IsUnset {
        get { return myState == TypeState.UNSET; }
    }

    public override string ToString() {
        return IsValid ? this.myValue : myState.ToString();
    }


    [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    StringType(SerializationInfo info, StreamingContext context) {
        myValue = (System.String)info.GetValue("myValue", typeof(System.String));
        myState = (TypeState)info.GetValue("myState", typeof(TypeState));
    }

    [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
        if (this.Equals(DEFAULT)) {
        info.SetType(typeof(StringType_DEFAULT));
        } else if (this.Equals(UNSET)) {
        info.SetType(typeof(StringType_UNSET));
        } else {
        info.SetType(typeof(StringType));
        info.AddValue("myValue", myValue);
        info.AddValue("myState", myState);
        }
    }
    }

    [Serializable]
    public class StringType_DEFAULT : IObjectReference {
    public object GetRealObject(StreamingContext context) {
        return StringType.DEFAULT;
    }
    }

    [Serializable]
    public class StringType_UNSET : IObjectReference {
    public object GetRealObject(StreamingContext context) {
        return StringType.UNSET;
    }
    }
}

Tests:

using System;

using NUnit.Framework;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Spring2.Core.Test.Serialization {

    /// <summary>
    /// Tests for BooleanType
    /// </summary>
    [TestFixture]
    public class DataTypeSerializationTest {


    [Test]
    public void ShouldBinarySerializeStringTypeWithValue() {
        BinaryFormatter binaryFmt = new BinaryFormatter();
        StringType s = new StringType("foo");

        FileStream fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
        binaryFmt.Serialize(fs, s);
        fs.Close();
        Console.WriteLine("Original value: {0}", s.ToString());

        // Deserialize.
        fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
        StringType s2 = (StringType)binaryFmt.Deserialize(fs);
        Console.WriteLine("New value: {0}", s2.ToString());
        fs.Close();

        Assert.AreEqual(s.ToString(), s2.ToString());
    }

    [Test]
    public void ShouldBinarySerializeStringTypeUnset() {
        BinaryFormatter binaryFmt = new BinaryFormatter();
        StringType s = StringType.UNSET;

        FileStream fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
        binaryFmt.Serialize(fs, s);
        fs.Close();
        Console.WriteLine("Original value is UNSET: {0}", s.IsUnset);

        // Deserialize.
        fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
        StringType s2 = (StringType)binaryFmt.Deserialize(fs);
        Console.WriteLine("new value is UNSET: {0}", s2.IsUnset);
        fs.Close();

        Assert.IsTrue(s2.IsUnset);
    }

    [Test]
    public void ShouldDeserializeDataObject() {
        BinaryFormatter binaryFmt = new BinaryFormatter();
        UserData u = new UserData();

        FileStream fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
        binaryFmt.Serialize(fs, u);
        fs.Close();
        Console.WriteLine("Original value is UNSET: {0}", u.Name.IsUnset);

        // Deserialize.
        fs = new FileStream("foo.dat", FileMode.OpenOrCreate);
        Object o = binaryFmt.Deserialize(fs);
        UserData u2 = (UserData)o;
        Console.WriteLine("new value is UNSET: {0}", u2.Name.IsUnset);
        fs.Close();
        Assert.IsTrue(Object.Equals(u, u2));
    }

    }


    [Serializable]
    public class UserData {
    private StringType name = StringType.DEFAULT;

    public StringType Name {
        get { return name; }
        set { name = value; }
    }
    }

}

Any help would be greatly appreciated!

Cort

+3
source share
3 answers

Cort, you must change the implementation of IObjectReference to return the structure. The third unit test will fail, but this does not comply with the statement of equal objects, and does not throw a type mismatch exception.

[Serializable]
public struct StringType_DEFAULT : IObjectReference {
public object GetRealObject(StreamingContext context) {
    return StringType.DEFAULT;
}
}

[Serializable]
public struct StringType_UNSET : IObjectReference {
public object GetRealObject(StreamingContext context) {
    return StringType.UNSET;
}
}
+2
source

You need to change StringType as a class instead of a structure.

+1

DEFAULT UNSET StringTypes readonly , . [: ].

Also, unless you have a specific logic that needs to happen, you do not need to implement ISerializable for this class. Attributing it as [Serializable] should be enough.

0
source

Source: https://habr.com/ru/post/1780966/


All Articles