シリアライズ(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を使ってオブジェクトをファイルにシリアライズし、再びデシリアライズしてみます。
using System;
using System.IO;
class Account {
public int ID;
public string Name;
public override string ToString()
{
return string.Format("{0}:{1}", ID, Name);
}
// シリアライズを行うメソッド
public void Serialize(Stream stream)
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(ID);
writer.Write(Name);
}
// デシリアライズを行うメソッド
public void Deserialize(Stream stream)
{
BinaryReader reader = new BinaryReader(stream);
ID = reader.ReadInt32();
Name = reader.ReadString();
}
}
class Sample {
static void Main()
{
// シリアライズするオブジェクトを作成
Account alice = new Account();
alice.ID = 2;
alice.Name = "Alice";
Console.WriteLine(alice);
// オブジェクトをファイルalice.binとしてシリアライズする
using (Stream stream = File.OpenWrite("alice.bin")) {
alice.Serialize(stream);
}
// ファイルの内容を元にデシリアライズする
Account deserialized = new Account();
using (Stream stream = File.OpenRead("alice.bin")) {
deserialized.Deserialize(stream);
}
Console.WriteLine(deserialized);
}
}
Imports System
Imports System.IO
Class Account
Public ID As Integer
Public Name As String
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", ID, Name)
End Function
' シリアライズを行うメソッド
Public Sub Serialize(ByVal stream As Stream)
Dim writer As New BinaryWriter(stream)
writer.Write(ID)
writer.Write(Name)
End Sub
' デシリアライズを行うメソッド
Public Sub Deserialize(ByVal stream As Stream)
Dim reader As New BinaryReader(stream)
ID = reader.ReadInt32()
Name = reader.ReadString()
End Sub
End Class
Class Sample
Shared Sub Main()
' シリアライズするオブジェクトを作成
Dim alice As New Account()
alice.ID = 2
alice.Name = "Alice"
Console.WriteLine(alice)
' オブジェクトをファイルalice.binとしてシリアライズする
Using stream As Stream = File.OpenWrite("alice.bin")
alice.Serialize(stream)
End Using
' ファイルの内容を元にデシリアライズする
Dim deserialized As New Account()
Using stream As Stream = File.OpenRead("alice.bin")
deserialized.Deserialize(stream)
End Using
Console.WriteLine(deserialized)
End Sub
End Class
2:Alice 2:Alice
この例のように、シリアライズする際の形式さえ定義すれば.NET Frameworkのシリアライザを使わなくてもシリアライズ・デシリアライズを行うことは可能です。 しかしこの方法では、シリアライズを行いたいクラス毎にシリアライズの形式を定義して実装する必要がある、多数のメンバ変数を含むオブジェクトのシリアライズを実装するのは面倒であるという問題があります。
シリアライザを用いたシリアライズ
続いて、.NET Frameworkで用意されているシリアライザを使ってシリアライズする場合について見てみます。 次の例は、先の例と同じクラスを、BinaryFormatterを使ってファイルにシリアライズし、再びデシリアライズする例です。 (BinaryFormatterについてはBinaryFormatterで解説します)
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
class Account {
public int ID;
public string Name;
public override string ToString()
{
return string.Format("{0}:{1}", ID, Name);
}
}
class Sample {
static void Main()
{
// シリアライズするオブジェクトを作成
Account alice = new Account();
alice.ID = 2;
alice.Name = "Alice";
Console.WriteLine(alice);
// オブジェクトをファイルalice.binとしてシリアライズ
using (Stream stream = File.OpenWrite("alice.bin")) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, alice);
}
// ファイルの内容を元にデシリアライズする
Account deserialized = null;
using (Stream stream = File.OpenRead("alice.bin")) {
BinaryFormatter formatter = new BinaryFormatter();
deserialized = (Account)formatter.Deserialize(stream);
}
Console.WriteLine(deserialized);
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable> _
Class Account
Public ID As Integer
Public Name As String
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", ID, Name)
End Function
End Class
Class Sample
Shared Sub Main()
' シリアライズするオブジェクトを作成
Dim alice As New Account()
alice.ID = 2
alice.Name = "Alice"
Console.WriteLine(alice)
' オブジェクトをファイルalice.binとしてシリアライズ
Using stream As Stream = File.OpenWrite("alice.bin")
Dim formatter As New BinaryFormatter()
formatter.Serialize(stream, alice)
End Using
' ファイルの内容を元にデシリアライズする
Dim deserialized As Account = Nothing
Using stream As Stream = File.OpenRead("alice.bin")
Dim formatter As New BinaryFormatter()
deserialized = CType(formatter.Deserialize(stream), Account)
End Using
Console.WriteLine(deserialized)
End Sub
End Class
2:Alice 2:Alice
この例において、シリアライズされるクラスにはSerializableという属性が付与されている以外、シリアライズ・デシリアライズに関するコードが記述されていない点に注目してください。 .NET Frameworkのシリアライザではリフレクションによってメンバ変数を自動的に探してシリアライズするようになっています。 そのため、シリアライズを行いたいクラス毎にシリアライズの形式を決める必要が無く、クラスの構造によらず同じ手順でシリアライズすることが出来るようになっています。
シリアライズの種類とシリアライザ
ここでは、いくつかのシリアライズの種類とシリアライザの使い方について簡単に見ていきます。 シリアライザの種類によっていくつかの制約があり、その違いを表にまとめると次のようになります。
BinaryFormatter
SoapFormatter |
XmlSerializer | |
---|---|---|
SerializableAttribute | 必須 | 付与されていなくてもよい |
シリアライズするクラス | パブリックでなくてもよい | バブリックでなければならない |
デフォルトコンストラクタ (既定のコンストラクタ) |
無くてもよい | 必須 |
シリアライズされるフィールド・プロパティ | すべてのフィールド・プロパティ | パブリックなフィールド・プロパティのみ |
型情報(アセンブリ名・型名) | シリアライズされる | シリアライズされない |
なお、BinaryFormatterおよびSoapFormatterは、シリアライズされるオブジェクトが自動実装プロパティ(C#、VB)を含んでいる場合でもバッキングフィールドの値を適切にシリアライズします。
BinaryFormatter
BinaryFormatter(System.Runtime.Serialization.Formatters.Binary名前空間)は、その名のとおりバイナリ形式でのシリアライズ・デシリアライズを行うシリアライザです。 Serializeメソッドで指定されたオブジェクトを任意のStreamにシリアライズ、DeserializeメソッドでStreamからオブジェクトにデシリアライズすることが出来ます。
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
class Account {
private int id;
public string Name;
public string ContactAddress {
get; set;
}
public Account(int id)
{
this.id = id;
}
public override string ToString()
{
return string.Format("{0}:{1} ({2})", id, Name, ContactAddress);
}
}
class Sample {
static void Main()
{
// シリアライズするオブジェクトを作成
Account alice = new Account(2);
alice.Name = "Alice";
alice.ContactAddress = "alice@mail.example.net";
Console.WriteLine(alice);
// オブジェクトをファイルalice.binとしてシリアライズ
using (Stream stream = File.OpenWrite("alice.bin")) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, alice);
}
// ファイルの内容を元にデシリアライズする
Account deserialized = null;
using (Stream stream = File.OpenRead("alice.bin")) {
BinaryFormatter formatter = new BinaryFormatter();
deserialized = (Account)formatter.Deserialize(stream);
}
Console.WriteLine(deserialized);
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable> _
Class Account
Private id As Integer
Public Name As String
Public Property ContactAddress As String
Public Sub New(ByVal id As Integer)
MyClass.id = id
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}:{1} ({2})", id, Name, ContactAddress)
End Function
End Class
Class Sample
Shared Sub Main()
' シリアライズするオブジェクトを作成
Dim alice As New Account(2)
alice.Name = "Alice"
alice.ContactAddress = "alice@mail.example.net"
Console.WriteLine(alice)
' オブジェクトをファイルalice.binとしてシリアライズ
Using stream As Stream = File.OpenWrite("alice.bin")
Dim formatter As New BinaryFormatter()
formatter.Serialize(stream, alice)
End Using
' ファイルの内容を元にデシリアライズする
Dim deserialized As Account = Nothing
Using stream As Stream = File.OpenRead("alice.bin")
Dim formatter As New BinaryFormatter()
deserialized = CType(formatter.Deserialize(stream), Account)
End Using
Console.WriteLine(deserialized)
End Sub
End Class
2:Alice (alice@mail.example.net) 2:Alice (alice@mail.example.net)
以下は、上記のコードで生成されるファイルの内容をダンプしたものです。 このファイルを生成した実行ファイル名はser.exeです。
0000000 \0 001 \0 \0 \0 377 377 377 377 001 \0 \0 \0 \0 \0 \0
0000020 \0 \f 002 \0 \0 \0 : s e r , V e r s
0000040 i o n = 0 . 0 . 0 . 0 , C u l
0000060 t u r e = n e u t r a l , P u
0000100 b l i c K e y T o k e n = n u l
0000120 l 005 001 \0 \0 \0 \a A c c o u n t 003 \0
0000140 \0 \0 002 i d 004 N a m e 037 < C o n t
0000160 a c t A d d r e s s > k _ _ B a
0000200 c k i n g F i e l d \0 001 001 \b 002 \0
0000220 \0 \0 002 \0 \0 \0 006 003 \0 \0 \0 005 A l i c
0000240 e 006 004 \0 \0 \0 026 a l i c e @ m a i
0000260 l . e x a m p l e . n e t \v
0000276
BinaryFormatterによるシリアライズ時の動作を制御する方法など、BinaryFormatterに関して詳しくはBinaryFormatter・SoapFormatterで解説しています。
SoapFormatter
SoapFormatter(System.Runtime.Serialization.Formatters.Soap名前空間)は、SOAP形式でのシリアライズ・デシリアライズを行うシリアライザです。 SOAPは通信プロトコルの一種で、XMLをベースとしたリモートプロシージャコールを行うためのプロトコルです。 なお、SoapFormatterを使用する場合はアセンブリSystem.Runtime.Serialization.Formatters.Soap.dllを参照する必要があります。
BinaryFormatterと同様、Serializeメソッドでシリアライズ、Deserializeメソッドでデシリアライズすることが出来ます。
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
[Serializable]
class Account {
private int id;
public string Name;
public string ContactAddress {
get; set;
}
public Account(int id)
{
this.id = id;
}
public override string ToString()
{
return string.Format("{0}:{1} ({2})", id, Name, ContactAddress);
}
}
class Sample {
static void Main()
{
// シリアライズするオブジェクトを作成
Account alice = new Account(2);
alice.Name = "Alice";
alice.ContactAddress = "alice@mail.example.net";
Console.WriteLine(alice);
// オブジェクトをファイルalice.soap.xmlとしてシリアライズ
using (Stream stream = File.OpenWrite("alice.soap.xml")) {
SoapFormatter formatter = new SoapFormatter();
formatter.Serialize(stream, alice);
}
// ファイルの内容を元にデシリアライズする
Account deserialized = null;
using (Stream stream = File.OpenRead("alice.soap.xml")) {
SoapFormatter formatter = new SoapFormatter();
deserialized = (Account)formatter.Deserialize(stream);
}
Console.WriteLine(deserialized);
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable> _
Class Account
Private id As Integer
Public Name As String
Public Property ContactAddress As String
Public Sub New(ByVal id As Integer)
MyClass.id = id
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}:{1} ({2})", id, Name, ContactAddress)
End Function
End Class
Class Sample
Shared Sub Main()
' シリアライズするオブジェクトを作成
Dim alice As New Account(2)
alice.Name = "Alice"
alice.ContactAddress = "alice@mail.example.net"
Console.WriteLine(alice)
' オブジェクトをファイルalice.binとしてシリアライズ
Using stream As Stream = File.OpenWrite("alice.bin")
Dim formatter As New BinaryFormatter()
formatter.Serialize(stream, alice)
End Using
' ファイルの内容を元にデシリアライズする
Dim deserialized As Account = Nothing
Using stream As Stream = File.OpenRead("alice.bin")
Dim formatter As New BinaryFormatter()
deserialized = CType(formatter.Deserialize(stream), Account)
End Using
Console.WriteLine(deserialized)
End Sub
End Class
2:Alice (alice@mail.example.net) 2:Alice (alice@mail.example.net)
以下は、上記のコードで生成されるファイルの内容をダンプしたものです。 このファイルを生成した実行ファイル名はser.exeです。
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:Account id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/assem/ser%2C%20Version%3D0.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<id>2</id>
<Name id="ref-3">Alice</Name>
<_x003C_ContactAddress_x003E_k__BackingField id="ref-4">alice@mail.example.net</_x003C_ContactAddress_x003E_k__BackingField>
</a1:Account>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SoapFormatterによるシリアライズ時の動作を制御する方法など、SoapFormatterに関して詳しくはBinaryFormatter・SoapFormatterで解説しています。
XmlSerializer
XmlSerializer(System.Xml.Serialization名前空間)は、XML Schemaに準拠したXML形式でのシリアライズ・デシリアライズを行うシリアライザです。 BinaryFormatterやSoapFormatterとは異なり、XmlSerializerのコンストラクタでシリアライズ・デシリアライズするオブジェクトの型を指定する必要があります。
シリアライズ・デシリアライズはSerializeメソッドとDeserializeメソッドで行いますが、BinaryFormatterやSoapFormatterとは異なり、StreamだけでなくXmlWriter/XmlReaderを使って行うことも出来ます。
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class Account {
private int id;
public string Name;
public string ContactAddress {
get; set;
}
// デフォルトコンストラクタ
public Account() {}
public Account(int id)
{
this.id = id;
}
public override string ToString()
{
return string.Format("{0}:{1} ({2})", id, Name, ContactAddress);
}
}
class Sample {
static void Main()
{
// シリアライズするオブジェクトを作成
Account alice = new Account(2);
alice.Name = "Alice";
alice.ContactAddress = "alice@mail.example.net";
Console.WriteLine(alice);
// オブジェクトをファイルalice.xmlとしてシリアライズ
using (Stream stream = File.OpenWrite("alice.xml")) {
XmlSerializer serializer = new XmlSerializer(typeof(Account));
serializer.Serialize(stream, alice);
}
// ファイルの内容を元にデシリアライズする
Account deserialized = null;
using (Stream stream = File.OpenRead("alice.xml")) {
XmlSerializer serializer = new XmlSerializer(typeof(Account));
deserialized = (Account)serializer.Deserialize(stream);
}
Console.WriteLine(deserialized);
}
}
Imports System
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
Public Class Account
Private id As Integer
Public Name As String
Public Property ContactAddress As String
' デフォルトコンストラクタ
Public Sub New()
End Sub
Public Sub New(ByVal id As Integer)
MyClass.id = id
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}:{1} ({2})", id, Name, ContactAddress)
End Function
End Class
Class Sample
Shared Sub Main()
' シリアライズするオブジェクトを作成
Dim alice As New Account(2)
alice.Name = "Alice"
alice.ContactAddress = "alice@mail.example.net"
Console.WriteLine(alice)
' オブジェクトをファイルalice.xmlとしてシリアライズ
Using stream As Stream = File.OpenWrite("alice.xml")
Dim serializer As New XmlSerializer(GetType(Account))
serializer.Serialize(stream, alice)
End Using
' ファイルの内容を元にデシリアライズする
Dim deserialized As Account = Nothing
Using stream As Stream = File.OpenRead("alice.xml")
Dim serializer As New XmlSerializer(GetType(Account))
deserialized = CType(serializer.Deserialize(stream), Account)
End Using
Console.WriteLine(deserialized)
End Sub
End Class
2:Alice (alice@mail.example.net) 0:Alice (alice@mail.example.net)
実行結果において、フィールドidの値が復元されていない点に注目してください。 以下は、上記のコードで生成されるファイルの内容をダンプしたものです。
<?xml version="1.0"?>
<Account xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Alice</Name>
<ContactAddress>alice@mail.example.net</ContactAddress>
</Account>
XmlSerializerによるシリアライズ時の動作を制御する方法など、XmlSerializerに関して詳しくはXmlSerializerで解説しています。
配列・コレクションのシリアライズ
XmlSerializerやBinaryFormatterでは単純なオブジェクトだけでなく、配列やコレクションをシリアライズすることも出来ます。 ただし、独自のコレクションクラスをシリアライズ・デシリアライズする場合は、以下の制限に注意する必要があります。
- IEnumerableを実装している場合、デシリアライズの際に要素を追加するためのパブリックなメソッドAddも用意している必要がある
- ICollectionを実装している場合は、上記に加え、シリアライズの際に要素を参照するためのインデクサ(Itemプロパティ)と、要素の数を取得するためのCountプロパティも用意している必要がある
- IEnumerable、ICollectionが返す値の型と、Addメソッドの引数の型は互換性がある型である必要がある
また当然ながら、配列・コレクションに格納されている個々の要素もシリアライズ可能でなければなりません。 以下の例は、XmlSerializerで配列およびListをシリアライズする例です。
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class Account {
public int ID;
public string Name;
// デフォルトコンストラクタ
public Account() {}
public Account(int id, string name)
{
this.ID = id;
this.Name = name;
}
public override string ToString()
{
return string.Format("{0}:{1}", ID, Name);
}
}
class Sample {
static void Main()
{
Account[] arr = new Account[] {
new Account(0, "Charlie"),
new Account(1, "Eve"),
new Account(2, "Alice"),
};
List<Account> list = new List<Account>(arr);
// Account[]をシリアライズ
using (Stream stream = File.OpenWrite("arr.xml")) {
XmlSerializer serializer = new XmlSerializer(arr.GetType());
serializer.Serialize(stream, arr);
}
// List<Account>をシリアライズ
using (Stream stream = File.OpenWrite("list.xml")) {
XmlSerializer serializer = new XmlSerializer(list.GetType());
serializer.Serialize(stream, list);
}
// Account[]をデシリアライズ
using (Stream stream = File.OpenRead("arr.xml")) {
XmlSerializer serializer = new XmlSerializer(typeof(Account[]));
Account[] deserialized = (Account[])serializer.Deserialize(stream);
Console.WriteLine("Account[]");
foreach (Account e in deserialized) {
Console.WriteLine(e);
}
}
// List<Account>をデシリアライズ
using (Stream stream = File.OpenRead("list.xml")) {
XmlSerializer serializer = new XmlSerializer(typeof(List<Account>));
List<Account> deserialized = (List<Account>)serializer.Deserialize(stream);
Console.WriteLine("List<Account>");
foreach (Account e in deserialized) {
Console.WriteLine(e);
}
}
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
Public Class Account
Public ID As Integer
Public Name As String
' デフォルトコンストラクタ
Public Sub New()
End Sub
Public Sub New(ByVal id As Integer, ByVal name As String)
MyClass.ID = id
MyClass.Name = name
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", ID, Name)
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As Account = New Account() { _
New Account(0, "Charlie"), _
New Account(1, "Eve"), _
New Account(2, "Alice") _
}
Dim list As New List(Of Account)(arr)
' Account()をシリアライズ
Using stream As Stream = File.OpenWrite("arr.xml")
Dim serializer As New XmlSerializer(arr.GetType())
serializer.Serialize(stream, arr)
End Using
' List(Of Account)をシリアライズ
Using stream As Stream = File.OpenWrite("list.xml")
Dim serializer As New XmlSerializer(list.GetType())
serializer.Serialize(stream, list)
End Using
' Account()をデシリアライズ
Using stream As Stream = File.OpenRead("arr.xml")
Dim serializer As New XmlSerializer(GetType(Account()))
Dim deserialized() As Account = CType(serializer.Deserialize(stream), Account())
Console.WriteLine("Account()")
For Each e As Account In deserialized
Console.WriteLine(e)
Next
End Using
' List(Of Account)をデシリアライズ
Using stream As Stream = File.OpenRead("list.xml")
Dim serializer As New XmlSerializer(GetType(List(Of Account)))
Dim deserialized As List(Of Account) = CType(serializer.Deserialize(stream), List(Of Account))
Console.WriteLine("List(Of Account)")
For Each e As Account In deserialized
Console.WriteLine(e)
Next
End Using
End Sub
End Class
Account[] 0:Charlie 1:Eve 2:Alice List<Account> 0:Charlie 1:Eve 2:Alice
以下は、上記のコードで生成されるファイルの内容をダンプしたものです。
<?xml version="1.0"?>
<ArrayOfAccount xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Account>
<ID>0</ID>
<Name>Charlie</Name>
</Account>
<Account>
<ID>1</ID>
<Name>Eve</Name>
</Account>
<Account>
<ID>2</ID>
<Name>Alice</Name>
</Account>
</ArrayOfAccount>
<?xml version="1.0"?>
<ArrayOfAccount xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Account>
<ID>0</ID>
<Name>Charlie</Name>
</Account>
<Account>
<ID>1</ID>
<Name>Eve</Name>
</Account>
<Account>
<ID>2</ID>
<Name>Alice</Name>
</Account>
</ArrayOfAccount>
なお、SoapFormatterではジェネリックな型をシリアライズすることは出来ず、List<T>などをシリアライズしようとすると以下のようにSerializationExceptionがスローされます。 そのため、List<T>はToArrayメソッドなどを使って一旦配列などの形にしてからシリアライズする必要があります。
ハンドルされていない例外: System.Runtime.Serialization.SerializationException: Soap シリアライザでは、ジェネリック タイプ System.Collections.Generic.List`1[Account] のシリアル化がサポートされていません。 場所 System.Runtime.Serialization.Formatters.Soap.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo) 場所 System.Runtime.Serialization.Formatters.Soap.ObjectWriter.Serialize(Object graph, Header[] inHeaders, SoapWriter serWriter) 場所 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers) 場所 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Serialize(Stream serializationStream, Object graph) 場所 Sample.Main()
クラスの継承とシリアライズ
SerializableAttributeはInheritedフィールドがfalseとなっているため、SerializableAttributeを付与したクラスを継承する場合は派生クラスでもSerializableAttributeを付与しないとシリアライズ可能とはなりません。
以下の例は、SoapFormatterを使ってSerializableAttributeを付与した派生クラスをシリアライズ・デシリアライズする例です。
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
[Serializable]
abstract class AccountBase {
protected int ID;
protected AccountBase(int id)
{
this.ID = id;
}
}
[Serializable]
class Account : AccountBase {
public string Name;
public Account(int id, string name)
: base(id)
{
this.Name = name;
}
public override string ToString()
{
return string.Format("{0}:{1}", ID, Name);
}
}
class Sample {
static void Main()
{
Account alice = new Account(2, "Alice");
Console.WriteLine(alice);
// シリアライズ
using (Stream stream = File.OpenWrite("alice.soap.xml")) {
SoapFormatter formatter = new SoapFormatter();
formatter.Serialize(stream, alice);
}
// デシリアライズ
using (Stream stream = File.OpenRead("alice.soap.xml")) {
SoapFormatter formatter = new SoapFormatter();
Account deserialized = (Account)formatter.Deserialize(stream);
Console.WriteLine(deserialized);
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Soap
<Serializable> _
MustInherit Class AccountBase
Protected ID As Integer
Protected Sub New(ByVal id As Integer)
MyClass.ID = id
End Sub
End Class
<Serializable> _
Class Account
Inherits AccountBase
Public Name As String
Public Sub New(ByVal id As Integer, ByVal name As String)
MyBase.New(id)
MyClass.Name = name
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", ID, Name)
End Function
End Class
Class Sample
Shared Sub Main()
Dim alice As New Account(2, "Alice")
Console.WriteLine(alice)
' シリアライズ
Using stream As Stream = File.OpenWrite("alice.soap.xml")
Dim formatter As New SoapFormatter()
formatter.Serialize(stream, alice)
End Using
' Account()をデシリアライズ
Using stream As Stream = File.OpenRead("alice.soap.xml")
Dim formatter As New SoapFormatter()
Dim deserialized As Account = CType(formatter.Deserialize(stream), Account)
Console.WriteLine(deserialized)
End Using
End Sub
End Class
2:Alice 2:Alice
この例で、基底クラス・派生クラスどちらか一方でもSerializableAttributeが付与されていなければ、以下のようにSerializationExceptionがスローされます。
ハンドルされていない例外: System.Runtime.Serialization.SerializationException: アセンブリ 'ser, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' の型 'Account' はシリアル化可能として設定されていません。 場所 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) 場所 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) 場所 System.Runtime.Serialization.Formatters.Soap.WriteObjectInfo.InitMemberInfo() 場所 System.Runtime.Serialization.Formatters.Soap.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, SoapAttributeInfo attributeInfo, ObjectWriter objectWriter) 場所 System.Runtime.Serialization.Formatters.Soap.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, SoapAttributeInfo attributeInfo, ObjectWriter objectWriter) 場所 System.Runtime.Serialization.Formatters.Soap.ObjectWriter.Serialize(Object graph, Header[] inHeaders, SoapWriter serWriter) 場所 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers) 場所 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Serialize(Stream serializationStream, Object graph) 場所 Sample.Main()
ハンドルされていない例外: System.Runtime.Serialization.SerializationException: アセンブリ 'ser, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' の型 'AccountBase' はシリアル化可能として設定されていません。 場所 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) 場所 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) 場所 System.Runtime.Serialization.Formatters.Soap.WriteObjectInfo.InitMemberInfo() 場所 System.Runtime.Serialization.Formatters.Soap.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, SoapAttributeInfo attributeInfo, ObjectWriter objectWriter) 場所 System.Runtime.Serialization.Formatters.Soap.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, SoapAttributeInfo attributeInfo, ObjectWriter objectWriter) 場所 System.Runtime.Serialization.Formatters.Soap.ObjectWriter.Serialize(Object graph, Header[] inHeaders, SoapWriter serWriter) 場所 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers) 場所 System.Runtime.Serialization.Formatters.Soap.SoapFormatter.Serialize(Stream serializationStream, Object graph) 場所 Sample.Main()
XmlSerializerはSerializableAttributeをチェックしないので、基底クラス・派生クラスともにSerializableAttributeは不要です。