哪种序列化程序对于 .NET 中序列化类型的更改最宽容?

发布于 2024-08-08 16:44:39 字数 174 浏览 5 评论 0原文

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

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

对于宽容选项,还有哪些其他替代方案,即不引发异常、仅使用默认值、跳过它们等?

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

I noticed the XmlSerializer is more forgiving to adding new members, removing existing ones, etc to the serialized types.

When I did this with the BinaryFormatter, and tried to deserialize the old data, it threw an exception.

What other alternatives are there for forgiving options, i.e. one that doesn't throw an exception just uses default values, skips them, etc?

Are protocol buffers forgiving in this regard?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

っ左 2024-08-15 16:44:39

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

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

它(与其他)基于数据契约,但不是 asdasd 等,而是使用数字标签来标识事物;所以:

[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;}

    // ...
}

我可以整天谈论这个主题,所以我最好在[为时已晚]之前闭嘴。

You mention binary, and indeed BinaryFormatter is very brittle here. The problem is that BinaryFormatter is type and field based. Instead, you want a contract-based serializer, such as XmlSerialzier, DataContractSerializer (3.0), etc.

Or for binary, protobuf-net is a C# implementation of Google's "protocol buffers" wire format, but re-implemented along .NET lines; (note: I'm the author...).

It is (like the others) data-contract based, but instead of <CustomerName>asdasd</CustomerName> etc, it uses numeric tags to identify things instead; so:

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

    // ...
}

As you add more members you give them new unique numbers; this keeps it extensible without relying on any names etc. Plus it is very fast ;-p As with XmlSerializer, it will ignore things it doesn't expect (or it can store them for safe round-trip of unexpected data), and supports the same default things. You can even use your existing xml attributes:

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

    // ...
}

I could talk about this subject all day, so I'd better shut up before [too late].

薯片软お妹 2024-08-15 16:44:39

您可以从 ISerialized 继承您的类并定义自定义 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 );
    }
}

You could inherit your class from ISerializable and define a custom GetObjectData. I haven't tested this, but such a class might be deserializable from a binary format, even if changes have since been made to the class.

EDIT

I just confirmed that this works. You can use code like the example below to explicitly define how an object is serialized and deserialized. It would then be up to you to make these methods work with older versions of your class. I tested this by serializing an instance of Cereal to a binary file, then making changes to the class and reading the file back in for deserialization.

[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 );
    }
}
总以为 2024-08-15 16:44:39

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

I strongly recommend doing your own serialization so that you have well-defined file formats independent of the language schemes.

撞了怀 2024-08-15 16:44:39

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

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

我通常创建一些想要用于序列化的简单数据类。当我需要更改类时,我实现 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; }
      }
   }

I actually find that the binary formatter is the most durable in the long run.

It provides excellent forward compatibility. That is to say, if you upgrade the file to a new version, it will not work with the old deserializer.

I generally create some simple data classes that I want to use for serialization. When i need to change the class, I implement the OnDeserialized / OnDeserializing methods. This allows the data to be upgraded.

The binary formatter does not require that you have a public setter for your properties, which to me is a big problem sometimes.

    [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; }
      }
   }
瞄了个咪的 2024-08-15 16:44:39

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

请参阅下面的帖子:

序列化和反序列化为 XML 文件, C#

I think the following post could help you. I also agree with others who said to write your own serializer. It is way better than generated code from xsd.exe .

See the post below:

Serialization and Deserialization into an XML file, C#

谎言 2024-08-15 16:44:39

您还可以查看 OptionalFieldAttributeSerializedAttribute/NonSerializedAttributeBinaryFormatterSoapFormatter

... 版本 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;
}

You can also look at the OptionalFieldAttribute for use with SerializableAttribute/NonSerializedAttribute and the BinaryFormatter and SoapFormatter

... version 1

[Serializable]
public class MyClass
{
    public string field1;

    [NonSerialized]
    public string field2;
}

... version 2

[Serializable]
public class MyClass
{        
    public string field1;

    [NonSerialized]
    public string field2;

    [OptionalField]
    public string field3;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文