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

Object配列やArrayListをXMLシリアル化する

注意:ここではXmlSerializerクラスを使った方法を紹介します。DataContractSerializerクラスを使った方法は、「DataContractSerializerを使って、Object配列やArrayListをXMLシリアル化する」で説明しています。

オブジェクトの内容をファイルに保存、復元する」で説明した方法でObject型の配列やArrayListオブジェクトなどをシリアル化すると、例外InvalidOperationException(XML ドキュメントを生成中にエラーが発生しました)がスローされる場合があります。例えば、次のようなケースです。

VB.NET
コードを隠すコードを選択
Public Class SampleItem
    Public Number As Integer
    Public Message As String

    Public Sub New()
        Number = 0
        Message = ""
    End Sub
    Public Sub New(num As Integer, msg As String)
        Number = num
        Message = msg
    End Sub
End Class

Public Class MainClass
    'エントリポイント
    Public Shared Sub Main()
        'XMLシリアル化するArrayList
        Dim al As New System.Collections.ArrayList()
        al.Add(New SampleItem(12, "こんにちは"))
        al.Add("こんばんは")

        'XMLファイルに保存する
        Dim serializer As New System.Xml.Serialization.XmlSerializer( _
            GetType(System.Collections.ArrayList))
        Dim sw As New System.IO.StreamWriter( _
            "C:\test\sample.xml", False, New System.Text.UTF8Encoding(False))
        'エラーが発生する
        serializer.Serialize(sw, al)
        '閉じる
        sw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System;

public class SampleItem
{
    public int Number;
    public string Message;

    public SampleItem()
    {
        Number = 0;
        Message = "";
    }
    public SampleItem(int num, string msg)
    {
        Number = num;
        Message = msg;
    }
}

public class MainClass
{
    //エントリポイント
    public static void Main()
    {
        //XMLシリアル化するArrayList
        System.Collections.ArrayList al = new System.Collections.ArrayList();
        al.Add(new SampleItem(12, "こんにちは"));
        al.Add("こんばんは");

        //XMLファイルに保存する
        System.Xml.Serialization.XmlSerializer serializer =
            new System.Xml.Serialization.XmlSerializer(
                typeof(System.Collections.ArrayList));
        System.IO.StreamWriter sw = new System.IO.StreamWriter(
            @"C:\test\sample.xml", false, new System.Text.UTF8Encoding(false));
        //エラーが発生する
        serializer.Serialize(sw, al);
        //閉じる
        sw.Close();
    }
}

ここでは、これを解決する方法を幾つか紹介します。

なおここでは「オブジェクトの内容をファイルに保存、復元する」で説明している事柄については一切説明しませんので、基本的な部分が分からないという場合は、まずそちらをご覧ください。

XmlSerializerコンストラクタに型を指定する方法

まずは、XmlSerializerのコンストラクタでArrayListに追加される可能性のあるすべてのオブジェクトの型を通知する方法を紹介します。例えば上記の例では、SampleItemクラス型とString型を通知します。復元する時も同様に型を通知する必要があります。

この方法を使って上記の例を修正すると次のようになります。(SampleItemクラスは前と同じですので、省略します。)

VB.NET
コードを隠すコードを選択
Public Class MainClass
    'エントリポイント
    Public Shared Sub Main()
        'XMLシリアル化するArrayList
        Dim al As New System.Collections.ArrayList()
        al.Add(New SampleItem(12, "こんにちは"))
        al.Add("こんばんは")

        'ArrayListに追加されているオブジェクトの型の配列を作成
        Dim et As Type() = New Type() {GetType(SampleItem)}

        'ArrayListに追加されているオブジェクトを指定してXMLファイルに保存する
        Dim serializer As New System.Xml.Serialization.XmlSerializer( _
            GetType(System.Collections.ArrayList), et)
        Dim sw As New System.IO.StreamWriter( _
            "C:\test\sample.xml", False, New System.Text.UTF8Encoding(False))
        serializer.Serialize(sw, al)
        '閉じる
        sw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
public class MainClass
{
    //エントリポイント
    public static void Main()
    {
        //XMLシリアル化するArrayList
        System.Collections.ArrayList al = new System.Collections.ArrayList();
        al.Add(new SampleItem(12, "こんにちは"));
        al.Add("こんばんは");

        //ArrayListに追加されているオブジェクトの型の配列を作成
        Type[] et = new Type[] { typeof(SampleItem) };

        //ArrayListに追加されているオブジェクトを指定してXMLファイルに保存する
        System.Xml.Serialization.XmlSerializer serializer =
            new System.Xml.Serialization.XmlSerializer(
                typeof(System.Collections.ArrayList), et);
        System.IO.StreamWriter sw = new System.IO.StreamWriter(
            @"C:\test\sample.xml", false, new System.Text.UTF8Encoding(false));
        serializer.Serialize(sw, al);
        //閉じる
        sw.Close();
    }
}

書き込まれたXMLファイルの内容は次のようになります。

XmlArrayItemAttributeを使用する方法

Object型の配列やArrayListがクラスのメンバで、そのクラスのインスタンスをシリアル化する場合は、そのメンバにXmlArrayItemAttribute属性を適用する方法があります。この方法はシリアル化するクラスに手を加えるだけで、あとは「オブジェクトの内容をファイルに保存、復元する」方法とまったく同じやり方でシリアル化できます。

以下に、ArrayListをメンバに持つSampleClassクラスのインスタンスをシリアル化する例を示します。

VB.NET
コードを隠すコードを選択
Public Class SampleItem
    Public Number As Integer
    Public Message As String

    Public Sub New()
        Number = 0
        Message = ""
    End Sub
    Public Sub New(num As Integer, msg As String)
        Number = num
        Message = msg
    End Sub
End Class

'シリアル化するクラス
Public Class SampleClass
    'ArrayListに追加される型を指定する
    <System.Xml.Serialization.XmlArrayItem(GetType(SampleItem)), _
    System.Xml.Serialization.XmlArrayItem(GetType(String))> _
    Public Items As System.Collections.ArrayList
End Class

Public Class MainClass
    'エントリポイント
    Public Shared Sub Main()
        'XMLシリアル化するオブジェクト
        Dim obj As New SampleClass()
        obj.Items = New System.Collections.ArrayList()
        obj.Items.Add(New SampleItem(12, "こんにちは"))
        obj.Items.Add("こんばんは")

        'ArrayListに追加されているオブジェクトを指定してXMLファイルに保存する
        Dim serializer 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))
        serializer.Serialize(sw, obj)
        '閉じる
        sw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System;

public class SampleItem
{
    public int Number;
    public string Message;

    public SampleItem()
    {
        Number = 0;
        Message = "";
    }
    public SampleItem(int num, string msg)
    {
        Number = num;
        Message = msg;
    }
}

//シリアル化するクラス
public class SampleClass
{
    //ArrayListに追加される型を指定する
    [System.Xml.Serialization.XmlArrayItem(typeof(SampleItem)),
    System.Xml.Serialization.XmlArrayItem(typeof(string))]
    public System.Collections.ArrayList Items;
}

public class MainClass
{
    //エントリポイント
    public static void Main()
    {
        //XMLシリアル化するオブジェクト
        SampleClass obj = new SampleClass();
        obj.Items = new System.Collections.ArrayList();
        obj.Items.Add(new SampleItem(12, "こんにちは"));
        obj.Items.Add("こんばんは");

        //ArrayListに追加されているオブジェクトを指定してXMLファイルに保存する
        System.Xml.Serialization.XmlSerializer serializer =
            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));
        serializer.Serialize(sw, obj);
        //閉じる
        sw.Close();
    }
}

このコードによって書き込まれたXMLファイルの内容は次のようになります。

要素名を変更する

例えば上記のシリアル化されたXMLで、<Items>や<SampleItem>、<string>の要素名を変更することもできます。それには、次のようにSampleClassクラスを書き換えます。

VB.NET
コードを隠すコードを選択
'シリアル化するクラス
Public Class SampleClass
    <System.Xml.Serialization.XmlArray("リスト")> _
    <System.Xml.Serialization.XmlArrayItem("サンプル", GetType(SampleItem)), _
    System.Xml.Serialization.XmlArrayItem("文字列", GetType(String))> _
    Public Items As System.Collections.ArrayList
End Class
C#
コードを隠すコードを選択
//シリアル化するクラス
public class SampleClass
{
    [System.Xml.Serialization.XmlArray("リスト")]
    [System.Xml.Serialization.XmlArrayItem("サンプル", typeof(SampleItem)),
    System.Xml.Serialization.XmlArrayItem("文字列", typeof(string))]
    public System.Collections.ArrayList Items;
}

すると、XMLは次のようになります。

要素名を変更する方法については、「オブジェクトの内容をファイルに保存、復元する」でさらに詳しく説明しています。

  • 履歴:
  • 2012/3/2 サンプルを分かりやすいように書き直すなど。
  • 2013/7/2 「要素名を変更する」を追加など。
  • 2014/5/7 FileStreamの代わりにStreamWriterを使うようにした。

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

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