2

この質問ごとに、シリアル化するオブジェクトがあります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 が含まれていません。考えられる原因は、無効なストリームまたはシリアル化と逆シリアル化の間のオブジェクト バージョンの変更です。

したがって、私の質問:オブジェクトのバージョンが変更される原因は何ですか? オブジェクトの 2 つのバージョン間で自分のボックスを行ったり来たり (新しいフィールドのコメント化/コメント解除) しても問題はありませんが、別の人のボックスでは最初の Deserialize() が爆発します。次のようにバージョンチェックをより寛容にしようとしましたが、どこから始めればよいかさえわかりません。

bf.AssemblyFormat =
    System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
4

1 に答える 1

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
于 2011-12-06T00:58:53.953 に答える