我注意到 XmlSerializer 在向序列化类型添加新成员、删除现有成员等方面更加宽容。
当我使用 BinaryFormatter 执行此操作并尝试反序列化旧数据时,它引发了异常。
还有哪些其他替代方案可以用于宽恕选项,即不抛出异常的方案只是使用默认值、跳过它们等?
协议缓冲区在这方面是否宽容?
我注意到 XmlSerializer 在向序列化类型添加新成员、删除现有成员等方面更加宽容。
当我使用 BinaryFormatter 执行此操作并尝试反序列化旧数据时,它引发了异常。
还有哪些其他替代方案可以用于宽恕选项,即不抛出异常的方案只是使用默认值、跳过它们等?
协议缓冲区在这方面是否宽容?
你提到二进制,这里确实BinaryFormatter
很脆弱。问题是BinaryFormatter
基于类型和字段的。XmlSerialzier
相反,您需要一个基于合约的序列化程序,例如DataContractSerializer
(3.0) 等。
或者对于二进制,protobuf-net是 Google 的“protocol buffers”有线格式的 C# 实现,但沿 .NET 行重新实现;(注:我是作者……)。
它(与其他人一样)基于数据合同,但不是<CustomerName>asdasd</CustomerName>
etc,而是使用数字标签来识别事物;所以:
[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; }
}
}
您还可以查看与SerializableAttribute / NonSerializedAttribute以及BinaryFormatter和SoapFormatter一起使用的OptionalFieldAttribute
...版本 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;
}