C Sharpの基礎 - シリアライズ
概要
クラスの内容をバイナリファイルに保存する簡単な方法として、BinaryFormatter
クラスを使う方法がある。
BinaryFormatter
クラスを使用してクラスをシリアル化して、FileStream
でファイルに書き込む。
BinaryFormatter
クラスの代わりにSoapFormatter
クラスを使って、XMLファイルに保存することもできる。
SoapFormatter
クラスの使い方は、ここで記載している方法とほぼ同様である。
バイナリファイル
シリアル化
以下に示すTestClassクラスを、BinaryFormatter
クラスを使用してバイナリファイルに保存する。
public class TestClass
{
private string _message;
private int _number;
public string Message
{
get {return _message;}
set {_message = value;}
}
public int Number
{
get {return _number;}
set {_number = value;}
}
public TestClass(string str, int num)
{
_message = str;
_number = num;
}
}
まず、以下のようにTestClassオブジェクトの先頭にSerializableAttribute
属性を付加する。
[Serializable()]
public class TestClass
{
// ...省略
}
SaveToBinaryFile
メソッドを使用して、バイナリファイルとしてシリアル化する。
また、LoadFromBinaryFile
メソッドを使用して、バイナリファイルから逆シリアル化する。
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public class MainClass
{
public static void Main()
{
// 保存先のファイル名
string fileName = @"C:\test.obj";
// TestClassオブジェクトを作成
TestClass obj1 = new TestClass("テスト", 123);
// オブジェクトの内容をファイルに保存する
SaveToBinaryFile(obj1, fileName);
// オブジェクトの内容をファイルから読み込み復元する
TestClass obj2 = (TestClass)LoadFromBinaryFile(fileName);
// 読み込んだオブジェクトの内容を表示
Console.WriteLine(obj2.Message);
Console.WriteLine(obj2.Number);
Console.ReadLine();
}
/// <summary>
/// オブジェクトの内容をファイルから読み込み復元する
/// </summary>
/// <param name="path">読み込むファイル名</param>
/// <returns>復元されたオブジェクト</returns>
public static object LoadFromBinaryFile(string path)
{
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryFormatter f = new BinaryFormatter();
// 読み込んで逆シリアル化する
object obj = f.Deserialize(fs);
fs.Close();
return obj;
}
/// <summary>
/// オブジェクトの内容をファイルに保存する
/// </summary>
/// <param name="obj">保存するオブジェクト</param>
/// <param name="path">保存先のファイル名</param>
public static void SaveToBinaryFile(object obj, string path)
{
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
BinaryFormatter bf = new BinaryFormatter();
// シリアル化して書き込む
bf.Serialize(fs, obj);
fs.Close();
}
}
シリアル化しないフィールドを指定する
上記のように、BinaryFormatter
クラスを使用してシリアル化できるものは、クラスのフィールドである。
この時、publicフィールド、protectedフィールド、privateフィールドも保存される。
保存しないフィールドには、NonSerializedAttribute
属性を付加する。
以下の例では、TestClassの_numberフィールドをシリアル化の対象から除外し、保存されないようにしている。
[Serializable()]
public class TestClass
{
private string _message;
[NonSerialized()]
private int _number;
// (省略)
}
XMLファイル
シリアル化できるもの
- 対象となるクラスの条件
- publicである
- publicではないクラスをシリアル化しようとすると、以下に示す
InvalidOperationException
例外が発生する。 "保護レベルの設定が原因で 'ProjectName.ClassName' にアクセスできません。パブリックの型のみ処理できます。"
- publicではないクラスをシリアル化しようとすると、以下に示す
- publicな既定のコンストラクタを有する
- publicな既定のコンストラクタを持たないクラスをシリアル化しようとすると、以下に示す
InvalidOperationException
例外が発生する。 "ProjectName.ClassName にはパラメーターを持たないコンストラクターが含まれていないため、これをシリアル化することはできません。"
- publicな既定のコンストラクタを持たないクラスをシリアル化しようとすると、以下に示す
- publicである
- 対象となる要素
- publicフィールド
- getアクセサおよびsetアクセサを有するpublicプロパティ
XMLの書式の変更
シリアル化の対象となるクラスおよびクラスのメンバに属性を付加することにより、保存するXMLファイルの書式を変更することができる。
属性 | 機能 | 適用前 | 適用後 |
---|---|---|---|
XmlAttribute | メンバをXMLの属性とする |
<ClassName>
<field>256</field>
</ClassName>
|
<ClassName field="256" />
|
XmlArray | 配列の名前を変更する |
<MemberName>
<TypeName>
<field>256</field>
</TypeName>
</MemberName>
|
<OriginalName>
<TypeName>
<field>256</field>
</TypeName>
</OriginalName>
|
XmlArrayItem | 配列の要素名を、型名から任意の名前に変更する。 |
<MemberName>
<TypeName>
<field>256</field>
</TypeName>
</MemberName>
|
<MemberName>
<OriginalName>
<field>256</field>
</TypeName>
</MemberName>
|
シリアル化
以下のクラスを、XMLファイルに保存する手順を記載する。
public class TestClass
{
private string _message;
private int _number;
public string Message
{
get {return _message;}
set {_message = value;}
}
public int Number
{
get {return _number;}
set {_number = value;}
}
public TestClass(string str, int num)
{
_message = str;
_number = num;
}
}
まず、次のようにTestClassオブジェクトにSerializableAttribute
属性を付加する。
[Serializable()]
public class TestClass
{
// (省略)
}
class Program
{
static void Main()
{
string fileName = "$HOME/Serialize.xml";
var myClass = new TestClass();
myClass.publicField = 1;
myClass.Property = 2;
using(var writer = new System.IO.StreamWriter(fileName))
{
// シリアル化の対象となるクラスの型を指定して XmlSerializerを作成する
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(TestClass));
// 指定のオブジェクトをシリアル化する
serializer.Serialize(writer, TestClass);
}
}
}
上記の例を実行する時、以下のようなXMLファイルが作成される。
<?xml version="1.0" encoding="utf-8"?>
<ClassName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<publicField>1</publicField>
<Property>2</Property>
</ClassName>
上記の例では、XmlSerializer
クラスのコンストラクタにType
クラスを指定しているが、これ以外にも下表に示すコンストラクタがある。
XmlSerializerクラスのコンストラクタ |
---|
XmlSerializer(Type) |
XmlSerializer(Type, String) |
XmlSerializer(Type, Type[]) |
XmlSerializer(Type, XmlAttributeOverrides) |
XmlSerializer(Type, XmlAttributeOverrides, Type[], XmlRootAttribute, String) |
XmlSerializer(Type, XmlAttributeOverrides, Type[], XmlRootAttribute, String, String) |
XmlSerializer(Type, XmlRootAttribute) |
XmlSerializer(XmlTypeMapping) |
※注意
XmlSerializer
クラスのインスタンスを生成する時、以下に示すFileNotFoundException
例外が発生することがある。
これは、XmlSerializer
クラスのアセンブリが用意されていないことが原因であり、sgen.exeファイルを実行して、このファイルを作成することで解決できる。
もしくは、このファイルが存在しない場合は自動的に作成されるため、この例外を無視しても問題ない。
ファイルまたはアセンブリ 'mscorlib.XmlSerializers, Version=x.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
配列のシリアル化
配列やListクラスの場合も、同様にシリアル化できる。
// int型配列
int[] array = new int[]{1, 2, 3};
var serializer = new XmlSerializer(typeof(int[]));
// Listクラス
List<int> list = new List<int>(new int[]{1, 2, 3});
var serializer = new XmlSerializer(typeof(List<int>));
これらは、以下のように出力される。
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<int>1</int>
<int>2</int>
<int>3</int>
</ArrayOfInt>
しかし、ArrayList
クラスのようなObject
型を返すクラスでは型を特定できないため、以下に示すInvalidOperationException
例外が発生する。
型 ProjectName.ClassName は指定されていません。スタティックに使用できない型を指定するには XmlInclude または SoapInclude 属性を使ってください。
この場合、XmlSerializer
クラスのコンストラクタにおいて、Object
型に含まれる可能性のある型を全て指定する。
XmlSerializerクラスのコンストラクタ (Type, Type[]) (System.Xml.Serialization) | MSDN
var obj = new ArrayList(new int[]{1, 2, 3});
var serializer = new XmlSerializer(typeof(ArrayList), new Type[]{typeof(int)});
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfAnyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<anyType xsi:type="xsd:int">1</anyType>
<anyType xsi:type="xsd:int">2</anyType>
<anyType xsi:type="xsd:int">3</anyType>
</ArrayOfAnyType>
逆シリアル化
作成されたXMLファイルは、逆シリアル化によりオブジェクトに復元できる。
以下の例では、XMLファイルをストリームから読み込み、オブジェクトに変換している。
Windowsの場合、逆シリアル化を行うには、環境変数TEMPにより定義される一時フォルダに書き込むため、アクセス権が必要となる場合がある。
class Program
{
static void Main()
{
string fileName = "$HOME/Serialize.xml";
TestClass myClass;
using(var reader = new System.IO.StreamReader(fileName))
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(TestClass));
myClass = (TestClass)serializer.Deserialize(reader);
}
}
}