DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

オブジェクトの内容をファイルに保存、復元する
オブジェクトのXMLシリアル化、逆シリアル化を行う

オブジェクトの内容をXMLファイルにして保存し、そのXMLファイルから元のオブジェクトに復元できると色々と便利です。例えば、INIファイルの代わりとして、設定を保存、復元したい時などに有用です。(設定を保存する場合は、「アプリケーションの設定を保存する」も参考にしてください。)

これを行うには、XmlSerializerクラス(System.Xml.Serialization名前空間)を使うと簡単です。XMLファイルに書き込む時はSerializeメソッドを使ってオブジェクトをXMLシリアル化(シリアライズ)し、XMLファイルを読み込む時はDeserializeメソッドを使ってXML逆シリアル化(デシリアライズ)をします。

オブジェクトの内容をXMLファイルに保存(シリアル化)する

次のコードでは、SampleClassオブジェクトの内容をXMLファイル(C:\test\sample.xml)に保存しています。ここではファイルの書き込みにStreamWriterクラスを使っていますが、StreamWriterの使い方が分からないという場合はまず「文字コードを指定してテキストファイルに書き込む」をご覧ください。

VB.NET
コードを隠すコードを選択
'XMLファイルに保存するオブジェクトのためのクラス
Public Class SampleClass
    Public Number As Integer
    Public Message As String
End Class

Class MainClass
    'SampleClassオブジェクトをXMLファイルに保存する
    Public Shared Sub Main()
        '保存先のファイル名
        Dim fileName As String = "C:\test\sample.xml"

        '保存するクラス(SampleClass)のインスタンスを作成
        Dim obj As New SampleClass()
        obj.Message = "テストです。"
        obj.Number = 123

        'XmlSerializerオブジェクトを作成
        'オブジェクトの型を指定する
        Dim serializer As New System.Xml.Serialization.XmlSerializer( _
            GetType(SampleClass))
        '書き込むファイルを開く(UTF-8 BOM無し)
        Dim sw As New System.IO.StreamWriter( _
            fileName, False, New System.Text.UTF8Encoding(False))
        'シリアル化し、XMLファイルに保存する
        serializer.Serialize(sw, obj)
        'ファイルを閉じる
        sw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
//XMLファイルに保存するオブジェクトのためのクラス
public class SampleClass
{
    public int Number;
    public string Message;
}

class MainClass
{
    //SampleClassオブジェクトをXMLファイルに保存する
    public static void Main()
    {
        //保存先のファイル名
        string fileName = @"C:\test\sample.xml";

        //保存するクラス(SampleClass)のインスタンスを作成
        SampleClass obj = new SampleClass();
        obj.Message = "テストです。";
        obj.Number = 123;

        //XmlSerializerオブジェクトを作成
        //オブジェクトの型を指定する
        System.Xml.Serialization.XmlSerializer serializer =
            new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
        //書き込むファイルを開く(UTF-8 BOM無し)
        System.IO.StreamWriter sw = new System.IO.StreamWriter(
            fileName, false, new System.Text.UTF8Encoding(false));
        //シリアル化し、XMLファイルに保存する
        serializer.Serialize(sw, obj);
        //ファイルを閉じる
        sw.Close();
    }
}
補足:インスタンスをシリアル化できるクラスには通常SerializableAttribute属性を適用しますが、上記の例のように、XmlSerializerではそうしなくてもシリアル化できます。
補足:以前紹介していたサンプルコードは、StreamWriterの代わりにFileStreamを使用していました。FileStreamだけを使用すると、作成されるXMLファイルのProcessingInstructionからencoding属性が省略され(つまり、「<?xml version="1.0" encoding="utf-8"?>」が「<?xml version="1.0"?>」に)、UTF-8で書き込まれます。ただし、UTF-8で書き込まれないというコメントを幾つかいただきましたので、環境等によってはそうならない可能性もあるのかもしれません。

上記の例により作成されたXMLファイルの内容は、次のようになります。

この方法によりXMLファイルに書き込まれるメンバは、パブリックメンバであるフィールドとプロパティだけです。さらに、読み込み、あるいは書き込みしか許可されていないプロパティは、パブリックメンバであっても書き込まれません。また、シリアル化するオブジェクトのクラスには、規定のコンストラクタ(パラメータを持たないコンストラクタ)が必要です。

XMLファイルの内容をオブジェクトに復元(逆シリアル化)する

次に、上記のコードにより保存されたXMLファイルから、SampleClassオブジェクトの内容を復元するコードを示します。ファイルの読み込みにはStreamReaderクラスを使っていますが、この詳細は「文字コードを指定してテキストファイルを読み込む」をご覧ください。

VB.NET
コードを隠すコードを選択
'XMLファイルに保存するオブジェクトのためのクラス
Public Class SampleClass
    Public Number As Integer
    Public Message As String
End Class

Class MainClass
    'XMLファイルをSampleClassオブジェクトに復元する
    Public Shared Sub Main()
        '保存元のファイル名
        Dim fileName As String = "C:\test\sample.xml"

        'XmlSerializerオブジェクトを作成
        Dim serializer As New System.Xml.Serialization.XmlSerializer( _
            GetType(SampleClass))
        '読み込むファイルを開く
        Dim sr As New System.IO.StreamReader( _
            fileName, New System.Text.UTF8Encoding(False))
        'XMLファイルから読み込み、逆シリアル化する
        Dim obj As SampleClass = _
            DirectCast(serializer.Deserialize(sr), SampleClass)
        'ファイルを閉じる
        sr.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
//XMLファイルに保存するオブジェクトのためのクラス
public class SampleClass
{
    public int Number;
    public string Message;
}

class MainClass
{
    //XMLファイルをSampleClassオブジェクトに復元する
    public static void Main()
    {
        //保存元のファイル名
        string fileName = @"C:\test\sample.xml";

        //XmlSerializerオブジェクトを作成
        System.Xml.Serialization.XmlSerializer serializer =
            new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
        //読み込むファイルを開く
        System.IO.StreamReader sr = new System.IO.StreamReader(
            fileName, new System.Text.UTF8Encoding(false));
        //XMLファイルから読み込み、逆シリアル化する
        SampleClass obj = (SampleClass)serializer.Deserialize(sr);
        //ファイルを閉じる
        sr.Close();
    }
}

配列をシリアル化、逆シリアル化する

シリアル化したいオブジェクトが配列やList<T>などのジェネリックコレクションであっても、やり方は全く同じです。

ただし、ArrayList、Hashtable、Dictionaryなどは、そのままではうまく行きません。これらに関しては、「Object配列やArrayListをXMLシリアル化する」や「HashtableやDictionaryオブジェクトをXMLシリアル化する」をご覧下さい。

以下に、SampleClassの配列をシリアル化、逆シリアル化する例を示します。

VB.NET
コードを隠すコードを選択
'次のクラスが宣言されているものとする
'Public Class SampleClass
'        Public Number As Integer
'        Public Message As String
'End Class

'保存する配列を作成
Dim ary As SampleClass() = New SampleClass(1) {}
For i As Integer = 0 To ary.Length - 1
    ary(i) = New SampleClass()
    ary(i).Number = i
    ary(i).Message = i.ToString() & "です。"
Next

'XMLファイルに保存する
Dim serializer1 As New System.Xml.Serialization.XmlSerializer( _
    GetType(SampleClass()))
Dim sw As New System.IO.StreamWriter( _
    "C:\test\sample.xml", False, New System.Text.UTF8Encoding(False))
serializer1.Serialize(sw, ary)
sw.Close()

'保存した内容を復元する
Dim serializer2 As New System.Xml.Serialization.XmlSerializer( _
    GetType(SampleClass()))
Dim sr As New System.IO.StreamReader( _
    "C:\test\sample.xml", New System.Text.UTF8Encoding(False))
Dim loadAry As SampleClass()
loadAry = DirectCast(serializer2.Deserialize(sr), SampleClass())
sr.Close()
C#
コードを隠すコードを選択
////次のクラスが宣言されているものとする
//public class SampleClass
//{
//    public int Number;
//    public string Message;
//}

//保存する配列を作成
SampleClass[] ary = new SampleClass[2];
for (int i = 0; i < ary.Length; i++)
{
    ary[i] = new SampleClass();
    ary[i].Number = i;
    ary[i].Message = i.ToString() + "です。";
}

//XMLファイルに保存する
System.Xml.Serialization.XmlSerializer serializer1 =
    new System.Xml.Serialization.XmlSerializer(typeof(SampleClass[]));
System.IO.StreamWriter sw = new System.IO.StreamWriter(
    @"C:\test\sample.xml", false, new System.Text.UTF8Encoding(false));
serializer1.Serialize(sw, ary);
sw.Close();

//保存した内容を復元する
System.Xml.Serialization.XmlSerializer serializer2 =
    new System.Xml.Serialization.XmlSerializer(typeof(SampleClass[]));
System.IO.StreamReader sr = new System.IO.StreamReader(
    @"C:\test\sample.xml", new System.Text.UTF8Encoding(false));
SampleClass[] loadAry;
loadAry = (SampleClass[])serializer2.Deserialize(sr);
sr.Close();

保存されたXMLファイルの内容は次のようになります。

XMLファイルに書き込まないメンバを指定する

ここからは、シリアル化した時にどのようにXMLに記述されるかを制御したいという方のための、より高度な話題になります。よって、必要ない方はお読み頂く必要はありません。

パブリックメンバであってもXMLファイルに書き込みたくないケースも考えられます。このような時は、書き込みたくないメンバにXmlIgnoreAttribute属性をつけます。先のSampleClassクラスを次のように書き換えることにより、SampleClassオブジェクトのMessageフィールドの値がXMLファイルに書き込まれなくなります。

VB.NET
コードを隠すコードを選択
'XMLファイルに保存するオブジェクトのためのクラス
Public Class SampleClass
    Public Number As Integer
    'このメンバの値はシリアル化しないようにする
    <System.Xml.Serialization.XmlIgnore()> _
    Public Message As String
End Class
C#
コードを隠すコードを選択
//XMLファイルに保存するオブジェクトのためのクラス
public class SampleClass
{
    public int Number;
    //このメンバの値はシリアル化しないようにする
    [System.Xml.Serialization.XmlIgnore]
    public string Message;
}

要素名を変更する

今までの例では、クラスの名前(SampleClass)とメンバの名前(NumberとMessage)がそのままXMLファイルの要素名に利用されていました。しかし、この要素名を別の名前に変えたいケースもあるでしょう。そのような時は、XmlRootAttribute及びXmlElementAttributeを指定します。

先のSampleClassクラスを次のように書き換え、"SampleClass"の代わりに"サンプル"、"Number"と"Message"の代わりに"数字"と"文字列"と、XMLファイルに出力するようにしています。

VB.NET
コードを隠すコードを選択
'XMLファイルに保存するオブジェクトのためのクラス
<System.Xml.Serialization.XmlRoot("サンプル")> _
Public Class SampleClass
    <System.Xml.Serialization.XmlElement("数字")> _
    Public Number As Integer
    <System.Xml.Serialization.XmlElement("文字列")> _
    Public Message As String
End Class
C#
コードを隠すコードを選択
//XMLファイルに保存するオブジェクトのためのクラス
[System.Xml.Serialization.XmlRoot("サンプル")]
public class SampleClass
{
    [System.Xml.Serialization.XmlElement("数字")]
    public int Number;
    [System.Xml.Serialization.XmlElement("文字列")]
    public string Message;
}

出力されるXMLファイルの内容は次のようになります。

XML属性、テキストとして保存する

メンバをXML属性として保存するにはXmlAttributeAttributeを、XMLテキストとして保存するにはXmlTextAttributeを使います。

VB.NET
コードを隠すコードを選択
'XMLファイルに保存するオブジェクトのためのクラス
<System.Xml.Serialization.XmlRoot("サンプル")> _
Public Class SampleClass
    <System.Xml.Serialization.XmlText()> _
    Public Number As Integer
    <System.Xml.Serialization.XmlAttribute("文字列")> _
    Public Message As String
End Class
C#
コードを隠すコードを選択
//XMLファイルに保存するオブジェクトのためのクラス
[System.Xml.Serialization.XmlRoot("サンプル")]
public class SampleClass
{
    [System.Xml.Serialization.XmlText]
    public int Number;
    [System.Xml.Serialization.XmlAttribute("文字列")]
    public string Message;
}

結果は次のようになります。

名前空間を指定する

XmlRootAttributeXmlAttributeAttributeのNamespaceプロパティによって、名前空間を指定することができます。

VB.NET
コードを隠すコードを選択
<System.Xml.Serialization.XmlRoot([Namespace]:="http://dobon.net")> _
Public Class SampleClass
    'パブリックメンバ
    Public Number As Integer
    Public Message As String
End Class
C#
コードを隠すコードを選択
[System.Xml.Serialization.XmlRoot(Namespace = "http://dobon.net")]
public class SampleClass
{
    //パブリックメンバ
    public int Number;
    public string Message;
}

配列メンバの要素名を変更する

例えば、以下のように配列(あるいは、List<T>などのジェネリックコレクションでも同じです)のメンバを持つクラスのインスタンスをシリアル化したとします。

VB.NET
コードを隠すコードを選択
Public Class SampleClass
    Public Samples As SampleItem() = New SampleItem() _
        {New SampleItem(), New SampleItem()}
End Class

Public Class SampleItem
    Public Number As Integer = 1
    Public Message As String = "いち"
End Class
C#
コードを隠すコードを選択
public class SampleClass
{
    public SampleItem[] Samples =
        new SampleItem[] { new SampleItem(), new SampleItem() };
}

public class SampleItem
{
    public int Number = 1;
    public string Message = "いち";
}

この時の結果は、以下のようになります。

もし配列メンバの要素名(上の例では、"Samples")を変更したいのであれば、XmlArrayAttribute属性を使います。また、配列要素の要素名(上の例では、"SampleItem")を変更したいのであれば、XmlArrayItemAttribute属性を使います。

これらの属性を使って、先ほどのSampleClassクラスを次のように書き換えたとします。

VB.NET
コードを隠すコードを選択
Public Class SampleClass
    <System.Xml.Serialization.XmlArray("リスト")> _
    <System.Xml.Serialization.XmlArrayItem("サンプル")> _
    Public Samples As SampleItem() = New SampleItem() _
        {New SampleItem(), New SampleItem()}
End Class
C#
コードを隠すコードを選択
public class SampleClass
{
    [System.Xml.Serialization.XmlArray("リスト")]
    [System.Xml.Serialization.XmlArrayItem("サンプル")]
    public SampleItem[] Samples =
        new SampleItem[] { new SampleItem(), new SampleItem() };
}

すると、シリアル化の結果は次のようになります。

ちなみに、XmlArrayAttributeとXmlArrayItemAttributeの代わりにXmlElementAttributeを使った時は、次のようになります。

VB.NET
コードを隠すコードを選択
Public Class SampleClass
    <System.Xml.Serialization.XmlElement("サンプル")> _
    Public Samples As SampleItem() = New SampleItem() _
        {New SampleItem(), New SampleItem()}
End Class
C#
コードを隠すコードを選択
public class SampleClass
{
    [System.Xml.Serialization.XmlElement("サンプル")]
    public SampleItem[] Samples =
        new SampleItem[] { new SampleItem(), new SampleItem() };
}

XmlArrayAttributeとXmlArrayItemAttribute使わずに、SampleItemクラスの方にXmlRootAttribute属性を使えば配列要素の要素名(SampleItem)を変更できそうですが、実際にはできません。

VB.NET
コードを隠すコードを選択
Public Class SampleClass
    Public Samples As SampleItem() = New SampleItem() _
        {New SampleItem(), New SampleItem()}
End Class

<System.Xml.Serialization.XmlRoot("サンプル")> _
Public Class SampleItem
    Public Number As Integer = 1
    Public Message As String = "いち"
End Class
C#
コードを隠すコードを選択
public class SampleClass
{
    public SampleItem[] Samples =
        new SampleItem[] { new SampleItem(), new SampleItem() };
}

[System.Xml.Serialization.XmlRoot("サンプル")]
public class SampleItem
{
    public int Number = 1;
    public string Message = "いち";
}

しかし、XmlRootAttributeの代わりにXmlTypeAttributeを使えば、できます。

VB.NET
コードを隠すコードを選択
Public Class SampleClass
    Public Samples As SampleItem() = New SampleItem() _
        {New SampleItem(), New SampleItem()}
End Class

<System.Xml.Serialization.XmlType("サンプル")> _
Public Class SampleItem
    Public Number As Integer = 1
    Public Message As String = "いち"
End Class
C#
コードを隠すコードを選択
public class SampleClass
{
    public SampleItem[] Samples =
        new SampleItem[] { new SampleItem(), new SampleItem() };
}

[System.Xml.Serialization.XmlType("サンプル")]
public class SampleItem
{
    public int Number = 1;
    public string Message = "いち";
}
  • 履歴:
  • 2012/2/24 「名前空間を指定する」を追加。
  • 2013/7/2 「XmlAttributeAttribute」とすべきところが「XmlTextAttribute」になっていたのを修正。「配列メンバの要素名を変更する」を追加など。
  • 2014/5/7 FileStreamの代わりにStreamWriterとStreamReaderを使うようにした。
  • 2014/8/6 XmlTextAttributeとXmlAttributeAttributeの説明が入れ替わっていたのを修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。