DOBON.NET

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ファイルの内容は次のようになります。

<?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="SampleItem">
    <Number>12</Number>
    <Message>こんにちは</Message>
  </anyType>
  <anyType xsi:type="xsd:string">こんばんは</anyType>
</ArrayOfAnyType>

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 version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Items>
    <SampleItem>
      <Number>12</Number>
      <Message>こんにちは</Message>
    </SampleItem>
    <string>こんばんは</string>
  </Items>
</SampleClass>

要素名を変更する

例えば上記のシリアル化された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は次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <リスト>
    <サンプル>
      <Number>12</Number>
      <Message>こんにちは</Message>
    </サンプル>
    <文字列>こんばんは</文字列>
  </リスト>
</SampleClass>

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

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

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

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

この記事への評価
良い / 悪い = 39 / 6

この記事へのコメント
通常のコメント [ 匿名 ] 2019年11月14日 15:42:18
XmlSerializerクラスで普通にList<>などもシリアル化できることに気づきませんでした。XmlSerializerクラスのリンクに誘導するようにしてはどうかと思います。

シリアル化 配列 等で検索するとここの記事に来ますが、List<>も簡単にXmlSerializerクラスで処理できることに気づません。 このページの対象がObject配列やArrayListと書いてありますが初心者はList<>とは違うとわからないのです

通常のコメント [ 匿名 ] 2019年2月3日 07:03:00
自己解決しました。同じ疑問を持った方にサンプルをのせておきます。
キャストすればいいだけでした。

SampleItem si;
Dictionary<string, string> dict = new Dictionary<string, string>();

int count = obj.Items.Count;
for (int i = 0; i < count; i++)
{
  si = (SampleItem)obj.Items[i];
dict[si.Number] = si.FileName;
}

通常のコメント [ 匿名 ] 2019年2月2日 14:01:03
obj.items[0]からMessageやNumberは取得できますでしょうか?
デバックでデシリライズされたobjをみたらMessageやNumberが見れました。
obj.items[0].Messageみたいな使い方ができるといいのですが……

通常のコメント [ 管理人 ] 2014年5月28日 15:51:38
> 配列と配列ではない値を格納するには、もう少し手順が必要になりますでしょうか。

「XmlArrayItemAttributeを使用する方法」のサンプルにはシリアル化するクラスにArrayList型のメンバしかありませんが、別の型のメンバを加えることもできますので、配列と配列でない値を格納することもできます。

通常のコメント [ Kino ] 2014年4月28日 02:28:40
配列と配列ではない値を格納するには、もう少し手順が必要になりますでしょうか。


この記事に関するコメントを投稿するには、下のボタンをクリックしてください。投稿フォームへ移動します。通常のご質問、ご意見等は掲示板へご投稿ください。