【问题标题】:Deserialization of optional fields from BinaryFormatterBinaryFormatter 中可选字段的反序列化
【发布时间】:2011-11-23 16:16:40
【问题描述】:

我有一个使用BinaryFormatter 序列化数据的应用程序。将成员添加到从一个版本序列化到下一个版本的类中,而不更改类名。添加了代码以处理旧序列化文件中可能缺少添加的成员:

private void readData(FileStream fs, SymmetricAlgorithm dataKey)
{
    CryptoStream cs = null;

    try
    {
        cs = new CryptoStream(fs, dataKey.CreateDecryptor(),
            CryptoStreamMode.Read);
        BinaryFormatter bf = new BinaryFormatter();

        string string1 = (string)bf.Deserialize(cs);
        // do stuff with string1

        bool bool1 = (bool)bf.Deserialize(cs);
        // do stuff with bool1

        ushort ushort1 = (ushort)bf.Deserialize(cs);
        // do stuff with ushort1

        // etc. etc. ...

        // this field was added later, so it may not be present
        // in the serialized binary data.  Check for it, and if
        // it's not there, do some default behavior

        NewStuffIncludedRecently newStuff = null;

        try
        {
            newStuff = (NewStuffIncludedRecently)bf.Deserialize(cs);
        }
        catch
        {
            newStuff = null;
        }

        _newStuff = newStuff != null ?
                new NewStuffIncludedRecently(newStuff) :
                new NewStuffIncludedRecently();
    }
    catch (Exception e)
    {
        // ...
    }
    finally
    {
        // ...
    }
}

我现在的意思是,我真的很想冲洗并与我想添加的另一个成员重复,这意味着我将添加另一个字段和 try-catch 块,类似于NewStuffIncludedRecently.

我曾想过只制作整个类[Serializable],但这不会破坏与旧序列化数据的兼容性吗?

我主要担心的是我不清楚反序列化是如何工作的。如果我添加与上述类似的另一个可选字段的处理,它会起作用吗?为了更好地处理这些更改,我还有哪些其他选择?

一如既往地提前致谢。

【问题讨论】:

  • 使用 binaryformatter 逐字段序列化感觉就像在大小上具有极大的开销。如果您想逐字段进行序列化,您应该只使用二进制编写器。我建议为旧版本 X 保留旧的序列化方案,并将任何新版本传递给基于 DataContracts 和/或协议缓冲区的方案。
  • 确实; BinaryFormatter 在版本控制时可能是一个巨大的痛苦;基于合约的序列化器往往更友好(XmlSerializer、DataContractSerializer、protobuf-net 等)。

标签: c# .net deserialization binaryformatter optional


【解决方案1】:

如果您用[OptionalField] 标记新字段,它应该可以工作,但我听说在某些情况下会出现问题。我不能肯定地说,因为我避免使用 BinaryFormatter,因为它在版本控制时有很多问题 :) (另外,它不像某些替代方案那样“紧密”,如果你想跨平台,它会有严重的问题, 或 CF/SL 等)

如果你正在实现ISerializable,你可以尝试:

foreach(SerializationEntry entry in info) {
    switch(entry.Name) {
         case "Name": Name = (string)info.Value;
         case "Id": Id = (int)info.Value;
         ...
    }
}

但是,必须再次强调 - 这是以艰难的方式做事:p 使用这种方法,您只需处理实际存在的数据。

【讨论】:

  • 感谢您的回答,马克 (+1)。在我的问题中不太清楚的一件事是,该类本身目前没有 [Serializable]。被序列化的单个事物被标记为 [Serializable](例如 NewStuffIncludedRecently),但包含类不是。如果我创建包含类 [Serializable],我会破坏向后兼容性吗?这不会在对象图中添加东西吗?
  • @John 如果它实现了 ISerializable,那么其他属性基本上会被忽略,完全由你来反序列化它,检查是否存在。如果它既不是 ISerializable 也不是 [Serializable],它就行不通,AFAIK。您可以枚举info 以查看它包含哪些字段;如果你想要一个例子,请告诉我。
  • @John 我添加了一个 SerializationEntry 示例
猜你喜欢
  • 1970-01-01
  • 2012-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
  • 2011-09-30
相关资源
最近更新 更多