カスタムのシリアル化Custom serialization

カスタムのシリアル化は、型のシリアル化と逆シリアル化を制御するプロセスです。Custom serialization is the process of controlling the serialization and deserialization of a type. シリアル化を制御することで、シリアル化の互換性を保証できます。つまり、型のコア機能を損なうことなく、1 つの型の複数のバージョン間でシリアル化および逆シリアル化を行うことができます。By controlling serialization, it's possible to ensure serialization compatibility, which is the ability to serialize and deserialize between versions of a type without breaking the core functionality of the type. たとえば、最初のバージョンの型では、フィールドが 2 つだけあるとします。For example, in the first version of a type, there may be only two fields. 新しいバージョンでは、これにいくつかのフィールドが追加されています。In the next version of a type, several more fields are added. この場合、2 番目のバージョンのアプリケーションでは、両方の型をシリアル化および逆シリアル化できる必要があります。Yet the second version of an application must be able to serialize and deserialize both types. 以下のセクションでは、シリアル化の制御方法について説明します。The following sections describe how to control serialization.

警告

バイナリ シリアル化は危険です。Binary serialization can be dangerous. 信頼できないソースからのデータの逆シリアル化、および管理下にないシステムへのラウンドトリップのシリアル化をしてはいけません。Never deserialize data from an untrusted source and never round-trip serialized data to systems not under your control.

重要

.NET Framework 4.0 よりも前のバージョンでは、部分的に信頼されたアセンブリでのカスタム ユーザー データのシリアル化は GetObjectData を使用して実行していました。In versions previous to .NET Framework 4.0, serialization of custom user data in a partially trusted assembly was accomplished using the GetObjectData. バージョン 4.0 以降では、そのメソッドは、部分的に信頼されたアセンブリで実行できないようにする SecurityCriticalAttribute 属性でマークされています。Starting with version 4.0, that method is marked with the SecurityCriticalAttribute attribute which prevents execution in partially trusted assemblies. この状況に対処するには、ISafeSerializationData インターフェイスを実装します。To work around this condition, implement the ISafeSerializationData interface.

シリアル化時およびシリアル化後のカスタム メソッドの実行Running custom methods during and after serialization

ベスト プラクティスかつ最も簡単な方法 (.Net Framework Version 2.0 で導入されました) は、シリアル化時およびシリアル化後にデータを修正するための各メソッドに、以下の属性を適用することです。The best practice and easiest way (introduced in version 2.0 of the .NET Framework) is to apply the following attributes to methods that are used to correct data during and after serialization:

これらの属性を適用すると、シリアル化プロセスと逆シリアル化プロセスの 4 つのフェーズのうち、いずれか、またはすべてに型を参加させることができます。These attributes allow the type to participate in any one of, or all four of the phases, of the serialization and deserialization processes. これらの属性は、各フェーズで呼び出す必要がある型のメソッドを指定します。The attributes specify the methods of the type that should be invoked during each phase. これらのメソッドはシリアル化ストリームにはアクセスしませんが、これらを使用すると、シリアル化の前後、または逆シリアル化の前後にオブジェクトを変更できます。The methods do not access the serialization stream but instead allow you to alter the object before and after serialization, or before and after deserialization. これらの属性は型の継承階層の全レベルで適用でき、各メソッドは、基本クラスから最派生クラスまで、階層内で呼び出されます。The attributes can be applied at all levels of the type inheritance hierarchy, and each method is called in the hierarchy from the base to the most derived. このしくみでは、最派生実装でシリアル化および逆シリアル化が行われるため、ISerializable インターフェイスの実装の複雑性やその実装によって発生する問題が回避されます。This mechanism avoids the complexity and any resulting issues of implementing the ISerializable interface by giving the responsibility for serialization and deserialization to the most derived implementation. また、フォーマッタは、フィールド値の設定およびシリアル化ストリームからの取得を無視できます。Additionally, this mechanism allows the formatters to ignore the population of fields and retrieval from the serialization stream. シリアル化および逆シリアル化の制御の詳細と例については、上記の各リンクをクリックしてください。For details and examples of controlling serialization and deserialization, click any of the previous links.

また、既存のシリアル化可能な型に新しいフィールドを追加する場合は、このフィールドに OptionalFieldAttribute 属性を適用します。In addition, when adding a new field to an existing serializable type, apply the OptionalFieldAttribute attribute to the field. 新しいフィールドが含まれていないストリームを処理する際に、BinaryFormatter および SoapFormatter はこのフィールドの不足を無視します。The BinaryFormatter and the SoapFormatter ignores the absence of the field when a stream that is missing the new field is processed.

ISerializable インターフェイスの実装Implementing the ISerializable interface

シリアル化を制御するもう 1 つの方法は、オブジェクトに ISerializable インターフェイスを実装することです。The other way to control serialization is achieved by implementing the ISerializable interface on an object. ただし、シリアル化の制御では、前のセクションで説明した方法がこの方法よりも優先されることに注意してください。Note, however, that the method in the previous section supersedes this method to control serialization.

また、Serializable 属性を使用してマークされ、クラス レベルまたはクラスのコンストラクターで宣言セキュリティまたは強制セキュリティが設定されたクラスでは、既定のシリアル化を使用しないでください。In addition, you should not use default serialization on a class that is marked with the Serializable attribute and has declarative or imperative security at the class level or on its constructors. 代わりに、このようなクラスでは、常に ISerializable インターフェイスを実装する必要があります。Instead, these classes should always implement the ISerializable interface.

ISerializable を実装すると、GetObjectData メソッドと、このオブジェクトが逆シリアル化されるときに使用される専用のコンストラクターも実装されます。Implementing ISerializable involves implementing the GetObjectData method and a special constructor that is used when the object is deserialized. 前のセクションで使用した ISerializable クラスに MyObject を実装する方法を次のコード例に示します。The following sample code shows how to implement ISerializable on the MyObject class from a previous section.

[Serializable]
public class MyObject : ISerializable
{
    public int n1;
    public int n2;
    public String str;

    public MyObject()
    {
    }

    protected MyObject(SerializationInfo info, StreamingContext context)
    {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("i", n1);
        info.AddValue("j", n2);
        info.AddValue("k", str);
    }
}
<Serializable()>  _
Public Class MyObject
    Implements ISerializable
    Public n1 As Integer
    Public n2 As Integer
    Public str As String

    Public Sub New()
    End Sub

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        n1 = info.GetInt32("i")
        n2 = info.GetInt32("j")
        str = info.GetString("k")
    End Sub 'New

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        info.AddValue("i", n1)
        info.AddValue("j", n2)
        info.AddValue("k", str)
    End Sub
End Class

シリアル化時に GetObjectData を呼び出す場合は、メソッド呼び出しで提供される SerializationInfo を設定する必要があります。When GetObjectData is called during serialization, you are responsible for populating the SerializationInfo provided with the method call. シリアル化の対象とする変数は、名前と値のペアとして追加します。Add the variables to be serialized as name and value pairs. 名前には、任意のテキストを使用できます。Any text can be used as the name. 逆シリアル化時にオブジェクトを復元するのに十分なデータがシリアル化される場合は、SerializationInfo に追加するメンバー変数を自由に決定できます。You have the freedom to decide which member variables are added to the SerializationInfo, provided that sufficient data is serialized to restore the object during deserialization. 基本オブジェクトが ISerializable を実装している場合、派生クラスはその基本オブジェクトの GetObjectData メソッドを呼び出す必要があります。Derived classes should call the GetObjectData method on the base object if the latter implements ISerializable.

シリアル化によって、他の方法ではアクセスできないオブジェクト インスタンス データを他のコードから参照または変更できるようになります。Note that serialization can allow other code to see or modify object instance data that is otherwise inaccessible. したがって、シリアル化を実行するコードには、SerializationFormatter フラグが指定された SecurityPermission が必要です。Therefore, code that performs serialization requires the SecurityPermission with the SerializationFormatter flag specified. 既定のポリシーでは、インターネットからダウンロードしたコードまたはイントラネット コードにはこのアクセス許可は与えられず、ローカル コンピューター上のコードにだけ付与されます。Under default policy, this permission is not given to Internet-downloaded or intranet code; only code on the local computer is granted this permission. GetObjectData メソッドは、SerializationFormatter フラグが指定された SecurityPermission を要求するか、特にプライベート データの保護に役立つ他のアクセス許可を要求することによって、明示的に保護する必要があります。The GetObjectData method must be explicitly protected either by demanding the SecurityPermission with the SerializationFormatter flag specified or by demanding other permissions that specifically help protect private data.

プライベート フィールドに機密情報が格納されている場合は、GetObjectData で適切なアクセス許可を要求してデータを保護してください。If a private field stores sensitive information, you should demand the appropriate permissions on GetObjectData to protect the data. SerializationFormatter フラグが指定された SecurityPermission を付与されているコードは、プライベート フィールドに格納されているデータを参照および変更できることに注意してください。Remember that code that has been granted SecurityPermission with the SerializationFormatter flag specified can view and modify the data stored in private fields. この SecurityPermission が付与されている悪意のある呼び出し元によって、隠しディレクトリの位置や付与されたアクセス許可などのデータが参照され、コンピューター上のセキュリティの脆弱性が悪用される可能性があります。A malicious caller granted this SecurityPermission can view data such as hidden directory locations or granted permissions and use the data to exploit a security vulnerability on the computer. 指定できるセキュリティ アクセス許可フラグの完全な一覧については、「SecurityPermissionFlag 列挙体」を参照してください。For a complete list of the security permission flags you can specify, see the SecurityPermissionFlag Enumeration.

ISerializable をクラスに追加する場合は、GetObjectData と専用のコンストラクターの両方を実装する必要があることに注意してください。It's important to stress that when ISerializable is added to a class you must implement both GetObjectData and the special constructor. GetObjectData がない場合、コンパイラから警告が出力されます。The compiler warns you if GetObjectData is missing. ただし、コンストラクターの実装を強制することはできないため、コンストラクターが指定されていなくても警告は表示されず、コンストラクターがないクラスの逆シリアル化が試行された時点で例外がスローされます。However, because it is impossible to enforce the implementation of a constructor, no warning is provided if the constructor is absent, and an exception is thrown when an attempt is made to deserialize a class without the constructor.

セキュリティやバージョン管理に関して発生する可能性がある問題を回避するために、現在のデザインは SetObjectData メソッドよりも優先されています。The current design was favored above a SetObjectData method to get around potential security and versioning problems. たとえば、SetObjectData メソッドは、インターフェイスの一部として定義された場合にはパブリックである必要があるため、ユーザーは SetObjectData メソッドが複数回呼び出されることを防ぐためにコードを記述する必要があります。For example, a SetObjectData method must be public if it is defined as part of an interface; thus users must write code to defend against having the SetObjectData method called multiple times. そうしないと、操作を実行しているオブジェクトで SetObjectData メソッドを呼び出す悪意のあるアプリケーションによって、さまざまな問題が発生する可能性があります。Otherwise, a malicious application that calls the SetObjectData method on an object in the process of executing an operation can cause potential problems.

逆シリアル化時に、SerializationInfo は、これをクラスに渡すために提供されているコンストラクターを使用してクラスに渡されます。During deserialization, SerializationInfo is passed to the class using the constructor provided for this purpose. オブジェクトが逆シリアル化されるときには、コンストラクターに対して設定された参照可能範囲の制限は無視されるため、パブリック、プロテクト、内部、またはプライベートとしてクラスをマークできます。Any visibility constraints placed on the constructor are ignored when the object is deserialized; so you can mark the class as public, protected, internal, or private. ただし、クラスがシールされている場合を除いて、コンストラクターをプロテクトにすることがベスト プラクティスです。クラスがシールされている場合は、コンストラクターをプライベートとマークする必要があります。However, it is a best practice to make the constructor protected unless the class is sealed, in which case the constructor should be marked private. コンストラクターは入力の検証も実行する必要があります。The constructor should also perform thorough input validation. 悪意のあるコードによる不適切な使用を回避するために、コンストラクターは、他のコンストラクターを使用してクラスのインスタンスを取得する場合と同様のセキュリティ チェックおよびアクセス許可を適用する必要があります。To avoid misuse by malicious code, the constructor should enforce the same security checks and permissions required to obtain an instance of the class using any other constructor. この推奨事項に従わないと、パブリック コンストラクターを使用する標準インスタンスの構築時に適用されるはずのセキュリティが適用されず、悪意のあるコードが、オブジェクトを事前にシリアル化し、SerializationFormatter フラグが指定された SecurityPermission で制御を取得して、クライアント コンピューター上でオブジェクトを逆シリアル化することが可能になります。If you do not follow this recommendation, malicious code can preserialize an object, obtain control with the SecurityPermission with the SerializationFormatter flag specified and deserialize the object on a client computer bypassing any security that would have been applied during standard instance construction using a public constructor.

オブジェクトの状態を復元するには、シリアル化時に使用した名前を使って、SerializationInfo から変数の値を取得します。To restore the state of the object, simply retrieve the values of the variables from SerializationInfo using the names used during serialization. 基本クラスに ISerializable が実装されている場合は、基本オブジェクトがその変数を復元できるようにするために、基本コンストラクターを呼び出す必要があります。If the base class implements ISerializable, the base constructor should be called to allow the base object to restore its variables.

ISerializable を実装しているクラスから新しいクラスを派生させるときに、派生クラスにシリアル化する必要がある変数がある場合は、派生クラスでコンストラクターと GetObjectData メソッドの両方を実装する必要があります。When you derive a new class from one that implements ISerializable, the derived class must implement both the constructor as well as the GetObjectData method if it has variables that need to be serialized. 上記の MyObject クラスを使用してこれを行う方法を次のコード例に示します。The following code example shows how this is done using the MyObject class shown previously.

[Serializable]
public class ObjectTwo : MyObject
{
    public int num;

    public ObjectTwo()
      : base()
    {
    }

    protected ObjectTwo(SerializationInfo si, StreamingContext context)
      : base(si, context)
    {
        num = si.GetInt32("num");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo si, StreamingContext context)
    {
        base.GetObjectData(si,context);
        si.AddValue("num", num);
    }
}
<Serializable()>  _
Public Class ObjectTwo
    Inherits MyObject
    Public num As Integer

    Public Sub New()

    End Sub

    Protected Sub New(ByVal si As SerializationInfo, _
    ByVal context As StreamingContext)
        MyBase.New(si, context)
        num = si.GetInt32("num")
    End Sub

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overrides Sub GetObjectData(ByVal si As SerializationInfo, ByVal context As StreamingContext)
        MyBase.GetObjectData(si, context)
        si.AddValue("num", num)
    End Sub
End Class

必ず、逆シリアル化コンストラクターで基本クラスを呼び出すようにしてください。Don't forget to call the base class in the deserialization constructor. そうしないと、基本クラスのコンストラクターが呼び出されず、逆シリアル化後にオブジェクトが完全には構築されません。If this isn't done, the constructor on the base class is never called, and the object is not fully constructed after deserialization.

オブジェクトは内側から外側に向かって再構築されるため、逆シリアル化時にメソッドを呼び出すと、望ましくない副作用を引き起こす可能性があります。これは、呼び出されるメソッドが、呼び出しの時点では逆シリアル化されていないオブジェクト参照を参照することがあるためです。Objects are reconstructed from the inside out; and calling methods during deserialization can have undesirable side effects, because the methods called might refer to object references that have not been deserialized by the time the call is made. 逆シリアル化対象のクラスで IDeserializationCallback を実装する場合、オブジェクト グラフ全体が逆シリアル化された時点で OnDeserialization メソッドが自動的に呼び出されます。If the class being deserialized implements the IDeserializationCallback, the OnDeserialization method is automatically called when the entire object graph has been deserialized. この時点で、参照されているすべての子オブジェクトが完全に復元されます。At this point, all the child objects referenced have been fully restored. ハッシュ テーブルは、イベント リスナーを使用せずに逆シリアル化することが困難なクラスの典型的な例です。A hash table is a typical example of a class that is difficult to deserialize without using the event listener. 逆シリアル化時にキーと値のペアを取得することは簡単ですが、これらのオブジェクトをハッシュ テーブルに戻すと、このハッシュ テーブルから派生したクラスが逆シリアル化されているかどうかわからないため、問題が発生する可能性があります。It is easy to retrieve the key and value pairs during deserialization, but adding these objects back to the hash table can cause problems, because there is no guarantee that classes that derived from the hash table have been deserialized. したがって、この段階でハッシュ テーブルのメソッドを呼び出すことはお勧めできません。Calling methods on a hash table at this stage is therefore not advisable.

関連項目See also