'OnDeserialization method was called while the object was not being deserialized.' 来自哈希表反序列化

'OnDeserialization method was called while the object was not being deserialized.' from hashtable deserialization

我有一个散列 table,用于存储我的应用程序在断电后正常工作所必需的 keys/values。它们使用 BinaryFormatter 存储到文件中。我试图通过仅将默认值类型放入哈希 table 来解释版本控制,例如 int、float、string、object 等,但也添加了一些枚举。

有时在更新应用程序时,散列 table 无法正确反序列化。我怀疑我使用的混淆器(所有枚举都被阻止进行符号重命名)或者某个字段以某种方式添加到我错过的对象中。反序列化调用:

var hashtable = new Hashtable();

                using (var stream = new FileStream(fileName, FileMode.Open))
                {
                    var formatter = new BinaryFormatter(selector, context);

                    try
                    {
                        hashtable = (Hashtable) formatter.Deserialize(stream);

为了解决这个问题,我制作了一个序列化代理 class,如下所示。反序列化时,我得到以下 SerializationException:'OnDeserialization method was called while the object was not being deserialized.'

[Serializable]
public class HashtableSerializationSurrogate : IDeserializationCallback, ISerializationSurrogate
{
    public HashtableSerializationSurrogate() : base()
    {
    }   

    private static SerializationInfo serializationInfo = null;

    void ISerializationSurrogate.GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        Hashtable hashtable = (Hashtable) obj;

        object[] objArray1 = new object[hashtable.Count];
        object[] objArray2 = new object[hashtable.Count];

        hashtable.Keys.CopyTo(objArray1, 0);
        hashtable.Values.CopyTo(objArray2, 0);

        // ISSUE: type reference
        info.AddValue("Version", 0, typeof(int));
        // ISSUE: type reference
        info.AddValue("Keys", objArray1, typeof(object[]));
        // ISSUE: type reference
        info.AddValue("Values", objArray2, typeof(object[]));

        // How much space to allocate to the hashtable.
        info.AddValue("HashSize", (fieldinfo.GetValue(obj) as Array).Length);

        serializationInfo = info;
    }


    private static FieldInfo fieldinfo = typeof(Hashtable).GetField("buckets", (BindingFlags) 36);

    object ISerializationSurrogate.SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        return obj;
    }

    void IDeserializationCallback.OnDeserialization(object sender)
    {
        var hashtable = (Hashtable)sender;
        hashtable.OnDeserialization(this);

        // Populate hash table
        object[] keys = (object[])serializationInfo.GetValue("Keys", typeof(object[]));
        object[] values = (object[])serializationInfo.GetValue("Values", typeof(object[]));

        // Add each (key,value] pair to the actual Hashtable
        for (int x = 0; x < keys.Length; x++)
            hashtable.Add(keys[x], values[x]);

        serializationInfo = null;
    }
}

我完全卡在这里了。有谁知道为什么在 SetObjectData 调用后抛出这个异常?

编辑:Stacktrace

我使用 source code 对此进行了一些研究。 首先要注意的是 Hashtable class 已经有一些 version control when serializing/deserializing,所以您可能不需要介绍自己的问题并在其他地方寻找问题的原因。

现在回答您的实际问题,异常是从 OnDeserialization 方法中抛出的,因为 SerializationInfoTable is empty. And it is empty because it gets filled only when calling the serialization constructor Hashtable(SerializationInfo info, StreamingContext context),在使用代理项时不会调用该方法。 SerializationInfoTable 是嵌套内部 class 中的内部 属性,所以你不能用它做太多事情。

您可以做的是创建一个继承的 class 并覆盖 GetObjectDataOnDeserialization 以在那里做您的事情。例如

        [Serializable]
    public class MyHashTable : Hashtable
    {
        private static SerializationInfo serializationInfo = null;

        protected MyHashTable(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }

        public MyHashTable() : base()
        {

        }
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("MyVersion", 0, typeof(int));

            serializationInfo = info;

        }

        public override void OnDeserialization(object sender)
        {
            base.OnDeserialization(sender);
            var myVersion = serializationInfo.GetValue("MyVersion", typeof(string));
            Console.WriteLine($"MyVersion: {myVersion}");
        }
    }
}

希望对您有所帮助。