【问题标题】:Which serializer is most forgiving for changes to the serialized types in .NET?哪个序列化程序对 .NET 中序列化类型的更改最宽容?
【发布时间】:2010-12-08 19:18:54
【问题描述】:

我注意到 XmlSerializer 在向序列化类型添加新成员、删除现有成员等方面更加宽容。

当我使用 BinaryFormatter 执行此操作并尝试反序列化旧数据时,它引发了异常。

还有哪些其他替代方案可用于宽恕选项,即不抛出异常只使用默认值、跳过它们等?

协议缓冲区在这方面是否宽容?

【问题讨论】:

    标签: c# .net serialization protocol-buffers


    【解决方案1】:

    您提到二进制,实际上BinaryFormatter 在这里是very brittle。问题是BinaryFormatter 是基于类型和字段的。相反,您需要一个基于合约的序列化程序,例如XmlSerialzierDataContractSerializer (3.0) 等。

    或者对于二进制,protobuf-net 是 Google 的“协议缓冲区”有线格式的 C# 实现,但沿 .NET 行重新实现; (注:我是作者……)。

    它(和其他人一样)基于数据合约,但不是<CustomerName>asdasd</CustomerName> 等,而是使用数字标签来标识事物;所以:

    [ProtoContract]
    public class Customer {
        [ProtoMember(1)]
        public string Name {get;set;}
    
        // ...
    }
    

    当您添加更多成员时,您会为他们提供新的唯一编号;这使它可以在不依赖任何名称等的情况下保持可扩展性。另外它非常快;-p 与XmlSerializer 一样,它会忽略它不期望的东西(或者它可以存储它们以安全地往返意外数据),并支持相同的默认事物。您甚至可以使用现有的 xml 属性:

    [XmlType]
    public class Customer {
        [XmlElement(Order=1)]
        public string Name {get;set;}
    
        // ...
    }
    

    我可以整天谈论这个话题,所以我最好在[太晚]之前闭嘴。

    【讨论】:

    • 谢谢 Marc,请随时多谈 :) 我想知道的一件事是,如果说您更改了类型并添加了新的唯一编号,您以后可以摆脱旧编号吗?就像您使用 1,2,3,4 和现在 5 来代替 2 的新成员一样。您以后可以用 2 来代表 5 吗?我只是认为这对我的目的可能更清洁?
    • 我从这条评论中学到了两件事。你是英国人,很自信。 :D
    • @Joan - 重复使用数字是不可取的,因为如果你得到一个未知年龄的数据,你就会有歧义......你使用旧合同还是新合同?只要您不尝试使用旧数据,它当然会工作...
    • 我想我们都希望马克尽可能多地说话 :)
    • 是的,我只用新的。基本上,这是一个个人应用程序,在我将其发布给公众之前,我不需要保持向后兼容性。所以在那之前,如果我真的改变了类型,如果我能在发布之前把它浓缩起来会很酷,所以至少看起来更干净。但是你会怎么做呢?可能需要几次迭代才能使用旧数字,对吧?
    【解决方案2】:

    您可以从 ISerializable 继承您的类并定义自定义 GetObjectData。我没有对此进行测试,但这样的类可能可以从二进制格式反序列化,即使已经对类进行了更改。

    编辑

    我刚刚确认这行得通。您可以使用类似下面示例的代码来显式定义对象的序列化和反序列化方式。然后由您来决定这些方法是否适用于您的类的旧版本。我通过将 Cereal 的实例序列化为二进制文件来测试这一点,然后对类进行更改并将文件读回以进行反序列化。

    [Serializable]
    private class Cereal : ISerializable
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public Cereal()
        {
        }
    
        protected Cereal( SerializationInfo info, StreamingContext context)
        {
            Id = info.GetInt32 ( "Id" );
            Name = info.GetString ( "Name" );
        }
    
        public void GetObjectData( SerializationInfo info, StreamingContext context )
        {
            info.AddValue ( "Id", Id );
            info.AddValue ( "Name", Name );
        }
    }
    

    【讨论】:

    • 是的,这正是 ISerializable 存在的原因——因此您可以处理以前的格式。
    【解决方案3】:

    我强烈建议您自己进行序列化,以便您拥有独立于语言方案的明确定义的文件格式。

    【讨论】:

      【解决方案4】:

      我实际上发现二进制格式化程序从长远来看是最耐用的。

      它提供了出色的前向兼容性。也就是说,如果您将文件升级到新版本,它将无法与旧的反序列化器一起使用。

      我通常会创建一些我想用于序列化的简单数据类。当我需要更改类时,我实现了 OnDeserialized / OnDeserializing 方法。这允许升级数据。

      二进制格式化程序不需要你的属性有一个公共设置器,这对我来说有时是个大问题。

          [Serializable]
          public class data
          {
            private int m_MyInteger;
            // New field
            private double m_MyDouble;
      
            [OnDeserializing]
            internal void OnDeserializing(StreamingContext context)
            {
              // some good default value
              m_MyDouble = 5;
            }
      
            public int MyInteger
            {
              get{ return m_MyInteger; }
              set { m_MyInteger = value; }
            }
         }
      

      【讨论】:

        【解决方案5】:

        我认为以下帖子可以帮助您。我也同意其他人说要编写自己的序列化程序的观点。它比从 xsd.exe 生成的代码要好得多。

        请看下面的帖子:

        Serialization and Deserialization into an XML file, C#

        【讨论】:

          【解决方案6】:

          您还可以查看OptionalFieldAttributeSerializableAttribute/NonSerializedAttribute 以及BinaryFormatterSoapFormatter 一起使用

          ...版本 1

          [Serializable]
          public class MyClass
          {
              public string field1;
          
              [NonSerialized]
              public string field2;
          }
          

          ...版本 2

          [Serializable]
          public class MyClass
          {        
              public string field1;
          
              [NonSerialized]
              public string field2;
          
              [OptionalField]
              public string field3;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-27
            • 2012-11-14
            • 2021-12-03
            • 1970-01-01
            • 2023-03-23
            • 1970-01-01
            相关资源
            最近更新 更多