Per this question我有一个我正在使用BinaryFormatter
序列化的对象。出于各种原因,我们已经实现了一个像这样的穷人版本处理,底部有一个try-catch块,用于较新版本的字段,但不是旧版本中的字段:
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会按照我的要求处理丢失的部分。
当我去同事的机器试图读取旧版本的对象时,会在顶部的第一个Deserialize()调用中抛出SerializationException:
二进制流'220'不包含有效的BinaryHeader。可能 原因是无效的流或对象版本之间的变化 序列化和反序列化。
因此我的问题:导致对象版本发生变化的原因是什么?当我在对象的两个版本之间在我的盒子上来回走动(评论/取消注释新字段)时没有问题,但在另一个人的盒子上第一个Deserialize()炸弹。我不确定哪里开始寻找,虽然我确实尝试让版本检查更加宽容,如此:
bf.AssemblyFormat =
System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
答案 0 :(得分:1)
我没有尝试使用BinaryFormatter(我们使用SoapFormatter),但我们解决这个问题的方法是实现类的手动序列化和反序列化,以便我们可以跳过问题属性。
例如,我们的每个可序列化类都包含以下内容(请原谅VB代码;如果需要,我可以转换):
' Serialization constructor
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
' This only needs to be called if this class inherits from a serializable class
'Call MyBase.New(info, context)
Call DeserializeObject(Me, GetType(ActionBase), info)
End Sub
' Ensure that the other class members that are not part of the NameObjectCollectionBase are serialized
Public Overridable Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) Implements ISerializable.GetObjectData
' This only needs to be called if this class inherits from a serializable class
'MyBase.GetObjectData(info, context)
Call SerializeObject(Me, GetType(ActionBase), info)
End Sub
然后,序列化和反序列化在所有类的标准方法中执行:
''' <summary>
''' This method is used to deserialize an object within the serialization constructor.
''' This is used when the caller does not want to explicitly and manually add every property to the
''' SerializationInfo object
''' </summary>
''' <param name="theObject"></param>
''' <param name="theType"></param>
''' <param name="theInfo"></param>
''' <remarks></remarks>
Public Sub DeserializeObject(ByVal theObject As Object, ByVal theType As Type, ByVal theInfo As SerializationInfo)
' Exceptions are handled by the caller
' Manually deserialize these items
With theType
If theInfo.MemberCount > 0 Then
For Each theField As FieldInfo In .GetFields(BindingFlags.DeclaredOnly Or BindingFlags.Instance Or BindingFlags.NonPublic)
Try
' Don't deserialize items that are marked as non-serialized
If Not theField.IsNotSerialized Then
If theField.FieldType.IsEnum Then
Try
theField.SetValue(theObject, System.Enum.Parse(theField.FieldType, CStr(theInfo.GetValue(theField.Name, theField.FieldType))))
Catch
theField.SetValue(theObject, TypeDescriptor.GetConverter(theField.FieldType).ConvertFrom(theInfo.GetInt32(theField.Name)))
End Try
Else
theField.SetValue(theObject, theInfo.GetValue(theField.Name, theField.FieldType))
End If
End If
Catch
End Try
Next
End If
End With
End Sub
''' <summary>
''' This method is used to serialize an object within the GetObjectData serialization method.
''' This is used when the caller does not want to explicitly and manually add every property to the
''' SerializationInfo object
''' </summary>
''' <param name="theObject"></param>
''' <param name="theType"></param>
''' <param name="theInfo"></param>
''' <remarks></remarks>
Public Sub SerializeObject(ByVal theObject As Object, ByVal theType As Type, ByVal theInfo As SerializationInfo)
' Exceptions are handled by the caller
' Manually serialize these items
With theType
For Each theField As FieldInfo In .GetFields(BindingFlags.DeclaredOnly Or BindingFlags.Instance Or BindingFlags.NonPublic)
' Don't serialize items that are marked as non-serialized
If Not theField.IsNotSerialized Then
theInfo.AddValue(theField.Name, theField.GetValue(theObject))
End If
Next
End With
End Sub