シリアライズ(serialize、永続化、シリアル化)とは実行中のアプリケーションにおけるオブジェクトの値(状態)を、バイナリ形式やXML形式などに変換して保存することを言います。 シリアライズすることによって、オブジェクトをファイルとして永続的に保存したり、ネットワーク経由で送信して別の場所で復元したりするといったことが出来るようになります。 シリアライズしたものをオブジェクトの形に復元することをデシリアライズ(deserialize、逆シリアル化)と言います。
.NET FrameworkのSystem.Runtime.Serialization名前空間およびSystem.Xml.Serialization名前空間には数種類のシリアライザと、その動作を制御するインターフェイス・属性が用意されています。 ここではシリアライズ・デシリアライズという用語を用いて、.NET Frameworkにおけるオブジェクトの永続化・復元について解説します。
なお、サンプルコードの一部でオブジェクト初期化子や自動実装プロパティなど言語のバージョンによってはサポートされていない機能を使っている箇所があります。 必要に応じて読み替え・書き換えを行ってください。
以下の解説で登場するBinaryFormatter/SoapFormatterについて、.NET 5以降においては使用は推奨されず、できるだけ早く使用をやめる必要があるとされています。 またこれを使用したコードでは、コンパイル時警告SYSLIB0011が出力されます。 特にASP.NET 5.0以降では、明示的にBinaryFormatterの使用を有効にしない限り常に例外NotSupportedExceptionがスローされます。
BinaryFormatter 型は危険であり、データ処理用としては "推奨されません"。 アプリケーションでは、処理するデータが信頼できると思われる場合でも、できるだけ早く BinaryFormatter の使用をやめる必要があります。 BinaryFormatter は安全ではなく、セキュリティで保護することはできません。
BinaryFormatter セキュリティ ガイド | Microsoft Docs
BinaryFormatter シリアル化メソッドが古い形式になり、ASP.NET アプリでは使用不可に
BinaryFormatter、Formatter、および IFormatter の Serialize と Deserialize のメソッドが古いと見なされ、警告が示されるようになりました。 また、ASP.NET アプリでは、BinaryFormatter のシリアル化が既定で禁止されます。
変更の説明
BinaryFormatter のセキュリティ脆弱性により、次のメソッドは古いと見なされ、ID SYSLIB0011 のコンパイル時警告が生成されるようになりました。 また、ASP.NET Core 5.0 以降のアプリでは、Web アプリによって BinaryFormatter 機能が再有効化されていない限り、NotSupportedException がスローされます。
基本クラス ライブラリの破壊的変更 - .NET Core | Microsoft Docs
- BinaryFormatter.Serialize
- BinaryFormatter.Deserialize
新規に作成するコードでは、BinaryFormatter/SoapFormatterの使用を避けるか、使用する場合は慎重に検討してください。 すでに使用しているコードでは、XmlSerializerやJsonSerializerの使用、あるいは他の手段への変更を検討してください。
シリアライザを使わないシリアライズ
.NET Frameworkにはシリアライズを行うためのクラス群が用意さていますが、まずはシリアライザを使わずにシリアライズを行う場合について見てみます。 簡単な例として、BinaryWriter/BinaryReaderを使ってオブジェクトをファイルにシリアライズし、再びデシリアライズしてみます。
この例のように、シリアライズする際の形式さえ定義すれば.NET Frameworkのシリアライザを使わなくてもシリアライズ・デシリアライズを行うことは可能です。 しかしこの方法では、シリアライズを行いたいクラス毎にシリアライズの形式を定義して実装する必要がある、多数のメンバ変数を含むオブジェクトのシリアライズを実装するのは面倒であるという問題があります。
シリアライザを用いたシリアライズ
続いて、.NET Frameworkで用意されているシリアライザを使ってシリアライズする場合について見てみます。 次の例は、先の例と同じクラスを、BinaryFormatterを使ってファイルにシリアライズし、再びデシリアライズする例です。 (BinaryFormatterについてはBinaryFormatterで解説します)
この例において、シリアライズされるクラスにはSerializableという属性が付与されている以外、シリアライズ・デシリアライズに関するコードが記述されていない点に注目してください。 .NET Frameworkのシリアライザではリフレクションによってメンバ変数を自動的に探してシリアライズするようになっています。 そのため、シリアライズを行いたいクラス毎にシリアライズの形式を決める必要が無く、クラスの構造によらず同じ手順でシリアライズすることが出来るようになっています。
シリアライズの種類とシリアライザ
ここでは、いくつかのシリアライズの種類とシリアライザの使い方について簡単に見ていきます。 シリアライザの種類によっていくつかの制約があり、その違いを表にまとめると次のようになります。
BinaryFormatter
SoapFormatter |
XmlSerializer | |
---|---|---|
SerializableAttribute | 必須 | 付与されていなくてもよい |
シリアライズするクラス | パブリックでなくてもよい | バブリックでなければならない |
デフォルトコンストラクタ (既定のコンストラクタ) |
無くてもよい | 必須 |
シリアライズされるフィールド・プロパティ | すべてのフィールド・プロパティ | パブリックなフィールド・プロパティのみ |
型情報(アセンブリ名・型名) | シリアライズされる | シリアライズされない |
なお、BinaryFormatterおよびSoapFormatterは、シリアライズされるオブジェクトが自動実装プロパティ(C#、VB)を含んでいる場合でもバッキングフィールドの値を適切にシリアライズします。
BinaryFormatter
BinaryFormatter(System.Runtime.Serialization.Formatters.Binary名前空間)は、その名のとおりバイナリ形式でのシリアライズ・デシリアライズを行うシリアライザです。 Serializeメソッドで指定されたオブジェクトを任意のStreamにシリアライズ、DeserializeメソッドでStreamからオブジェクトにデシリアライズすることが出来ます。
以下は、上記のコードで生成されるファイルの内容をダンプしたものです。 このファイルを生成した実行ファイル名はser.exeです。
BinaryFormatterによるシリアライズ時の動作を制御する方法など、BinaryFormatterに関して詳しくはBinaryFormatter・SoapFormatterで解説しています。
SoapFormatter
SoapFormatter(System.Runtime.Serialization.Formatters.Soap名前空間)は、SOAP形式でのシリアライズ・デシリアライズを行うシリアライザです。 SOAPは通信プロトコルの一種で、XMLをベースとしたリモートプロシージャコールを行うためのプロトコルです。 なお、SoapFormatterを使用する場合はアセンブリSystem.Runtime.Serialization.Formatters.Soap.dllを参照する必要があります。
BinaryFormatterと同様、Serializeメソッドでシリアライズ、Deserializeメソッドでデシリアライズすることが出来ます。
以下は、上記のコードで生成されるファイルの内容をダンプしたものです。 このファイルを生成した実行ファイル名はser.exeです。
SoapFormatterによるシリアライズ時の動作を制御する方法など、SoapFormatterに関して詳しくはBinaryFormatter・SoapFormatterで解説しています。
XmlSerializer
XmlSerializer(System.Xml.Serialization名前空間)は、XML Schemaに準拠したXML形式でのシリアライズ・デシリアライズを行うシリアライザです。 BinaryFormatterやSoapFormatterとは異なり、XmlSerializerのコンストラクタでシリアライズ・デシリアライズするオブジェクトの型を指定する必要があります。
シリアライズ・デシリアライズはSerializeメソッドとDeserializeメソッドで行いますが、BinaryFormatterやSoapFormatterとは異なり、StreamだけでなくXmlWriter/XmlReaderを使って行うことも出来ます。
実行結果において、フィールドidの値が復元されていない点に注目してください。 以下は、上記のコードで生成されるファイルの内容をダンプしたものです。
XmlSerializerによるシリアライズ時の動作を制御する方法など、XmlSerializerに関して詳しくはXmlSerializerで解説しています。
配列・コレクションのシリアライズ
XmlSerializerやBinaryFormatterでは単純なオブジェクトだけでなく、配列やコレクションをシリアライズすることも出来ます。 ただし、独自のコレクションクラスをシリアライズ・デシリアライズする場合は、以下の制限に注意する必要があります。
- IEnumerableを実装している場合、デシリアライズの際に要素を追加するためのパブリックなメソッドAddも用意している必要がある
- ICollectionを実装している場合は、上記に加え、シリアライズの際に要素を参照するためのインデクサ(Itemプロパティ)と、要素の数を取得するためのCountプロパティも用意している必要がある
- IEnumerable、ICollectionが返す値の型と、Addメソッドの引数の型は互換性がある型である必要がある
また当然ながら、配列・コレクションに格納されている個々の要素もシリアライズ可能でなければなりません。 以下の例は、XmlSerializerで配列およびListをシリアライズする例です。
以下は、上記のコードで生成されるファイルの内容をダンプしたものです。
なお、SoapFormatterではジェネリックな型をシリアライズすることは出来ず、List<T>などをシリアライズしようとすると以下のようにSerializationExceptionがスローされます。 そのため、List<T>はToArrayメソッドなどを使って一旦配列などの形にしてからシリアライズする必要があります。
クラスの継承とシリアライズ
SerializableAttributeはInheritedフィールドがfalseとなっているため、SerializableAttributeを付与したクラスを継承する場合は派生クラスでもSerializableAttributeを付与しないとシリアライズ可能とはなりません。
以下の例は、SoapFormatterを使ってSerializableAttributeを付与した派生クラスをシリアライズ・デシリアライズする例です。
この例で、基底クラス・派生クラスどちらか一方でもSerializableAttributeが付与されていなければ、以下のようにSerializationExceptionがスローされます。
XmlSerializerはSerializableAttributeをチェックしないので、基底クラス・派生クラスともにSerializableAttributeは不要です。