【发布时间】:2010-12-08 19:18:54
【问题描述】:
我注意到 XmlSerializer 在向序列化类型添加新成员、删除现有成员等方面更加宽容。
当我使用 BinaryFormatter 执行此操作并尝试反序列化旧数据时,它引发了异常。
还有哪些其他替代方案可用于宽恕选项,即不抛出异常只使用默认值、跳过它们等?
协议缓冲区在这方面是否宽容?
【问题讨论】:
标签: c# .net serialization protocol-buffers
我注意到 XmlSerializer 在向序列化类型添加新成员、删除现有成员等方面更加宽容。
当我使用 BinaryFormatter 执行此操作并尝试反序列化旧数据时,它引发了异常。
还有哪些其他替代方案可用于宽恕选项,即不抛出异常只使用默认值、跳过它们等?
协议缓冲区在这方面是否宽容?
【问题讨论】:
标签: c# .net serialization protocol-buffers
您提到二进制,实际上BinaryFormatter 在这里是very brittle。问题是BinaryFormatter 是基于类型和字段的。相反,您需要一个基于合约的序列化程序,例如XmlSerialzier、DataContractSerializer (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;}
// ...
}
我可以整天谈论这个话题,所以我最好在[太晚]之前闭嘴。
【讨论】:
您可以从 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 );
}
}
【讨论】:
我强烈建议您自己进行序列化,以便您拥有独立于语言方案的明确定义的文件格式。
【讨论】:
我实际上发现二进制格式化程序从长远来看是最耐用的。
它提供了出色的前向兼容性。也就是说,如果您将文件升级到新版本,它将无法与旧的反序列化器一起使用。
我通常会创建一些我想用于序列化的简单数据类。当我需要更改类时,我实现了 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; }
}
}
【讨论】:
我认为以下帖子可以帮助您。我也同意其他人说要编写自己的序列化程序的观点。它比从 xsd.exe 生成的代码要好得多。
请看下面的帖子:
【讨论】:
您还可以查看OptionalFieldAttribute 与SerializableAttribute/NonSerializedAttribute 以及BinaryFormatter 和SoapFormatter 一起使用
...版本 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;
}
【讨论】: