DOBON.NET

DataContractSerializerを使って、Object配列やArrayListをXMLシリアル化する

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

DataContractSerializerを使って、オブジェクトのXMLシリアル化、逆シリアル化を行う」の方法では、Object型の配列や、ArrayListオブジェクト、あるいはObject型のプロパティを持つオブジェクトなどをシリアル化する時、例外System.Runtime.Serialization.SerializationExceptioがスローされる場合があります。例えば以下のようなケースです。

VB.NET
コードを隠すコードを選択
Imports System.Runtime.Serialization
Imports System.Xml
Imports System.Collections

<DataContract> _
Public Class SampleItem
    <DataMember> _
    Public Number As Integer
    <DataMember> _
    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()
        '保存先のファイル名
        Dim fileName As String = "C:\test\test.xml"

        'シリアル化するArrayList
        Dim al As New ArrayList()
        al.Add(New SampleItem(12, "こんにちは"))

        'シリアル化する
        Dim serializer As New DataContractSerializer(GetType(ArrayList))
        Dim settings As New XmlWriterSettings()
        settings.Encoding = New System.Text.UTF8Encoding(False)
        Dim xw As XmlWriter = XmlWriter.Create(fileName, settings)
        'SerializationExceptioがスローされる
        serializer.WriteObject(xw, al)
        xw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System.Runtime.Serialization;
using System.Xml;
using System.Collections;

[DataContract]
public class SampleItem
{
    [DataMember]
    public int Number;
    [DataMember]
    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()
    {
        //保存先のファイル名
        string fileName = @"C:\test\test.xml";

        //シリアル化するArrayList
        ArrayList al = new ArrayList();
        al.Add(new SampleItem(12, "こんにちは"));

        //シリアル化する
        DataContractSerializer serializer =
            new DataContractSerializer(typeof(ArrayList));
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Encoding = new System.Text.UTF8Encoding(false);
        XmlWriter xw = XmlWriter.Create(fileName, settings);
        //SerializationExceptioがスローされる
        serializer.WriteObject(xw, al);
        xw.Close();
    }
}

ここでは、このような場合の対処法を紹介します。

認識すべき型をDataContractSerializerのコンストラクタに指定する

まず、ArrayListに追加されているすべての型のコレクションをDataContractSerializerのコンストラクタに指定するという解決法があります。

この方法によって上記の例を書き換えると、次のようになります。書き換えたのは、Typeの配列を作成する部分と、DataContractSerializerのコンストラクタを呼び出す部分だけです。

VB.NET
コードを隠すコードを選択
Imports System.Runtime.Serialization
Imports System.Xml
Imports System.Collections

<DataContract> _
Public Class SampleItem
    <DataMember> _
    Public Number As Integer
    <DataMember> _
    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()
        Dim fileName As String = "C:\test\test.xml"
        Dim al As New ArrayList()
        al.Add(New SampleItem(12, "こんにちは"))

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

        '予想される型を指定して、DataContractSerializerを作成
        Dim serializer As New DataContractSerializer( _
            GetType(ArrayList), knownTypes)

        'シリアル化できるようになる
        Dim settings As New XmlWriterSettings()
        settings.Encoding = New System.Text.UTF8Encoding(False)
        Dim xw As XmlWriter = XmlWriter.Create(fileName, settings)
        serializer.WriteObject(xw, al)
        xw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System;
using System.Runtime.Serialization;
using System.Xml;
using System.Collections;

[DataContract]
public class SampleItem
{
    [DataMember]
    public int Number;
    [DataMember]
    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()
    {
        string fileName = @"C:\test\test.xml";
        ArrayList al = new ArrayList();
        al.Add(new SampleItem(12, "こんにちは"));

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

        //予想される型を指定して、DataContractSerializerを作成
        DataContractSerializer serializer =
            new DataContractSerializer(typeof(ArrayList), knownTypes);

        //シリアル化できるようになる
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Encoding = new System.Text.UTF8Encoding(false);
        XmlWriter xw = XmlWriter.Create(fileName, settings);
        serializer.WriteObject(xw, al);
        xw.Close();
    }
}

上記のコードで作成されるXMLファイルの内容は、以下のようになります(見やすいように改行を入れています)。

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<anyType xmlns:d2p1="http://schemas.datacontract.org/2004/07/" i:type="d2p1:SampleItem">
<d2p1:Message>こんにちは</d2p1:Message>
<d2p1:Number>12</d2p1:Number></anyType>
</ArrayOfanyType>

KnownTypeAttributeを使用する

リシアル化したいオブジェクトのメンバがObjectやObject配列、コレクションである場合は、そのクラスにKnownTypeAttributeを適用して、認識すべき型を指定しておく方法もあります。このようなクラスを作れば、通常の方法でシリアル化できるようになります。

以下に例を示します。ここではSampleClassクラスにKnownTypeAttributeを適用し、SampleClassオブジェクトをシリアル化できるようにしています。

VB.NET
コードを隠すコードを選択
Imports System.Runtime.Serialization
Imports System.Xml
Imports System.Collections

<DataContract> _
Public Class SampleItem
    <DataMember> _
    Public Number As Integer
    <DataMember> _
    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

'KnownTypeAttributeを適用し、シリアル化の時に認識すべき型を指定する
<DataContract> _
<KnownType(GetType(SampleItem))> _
Public Class SampleClass
    <DataMember> _
    Public Items As System.Collections.ArrayList

    Public Sub New()
        Items = New System.Collections.ArrayList()
        Items.Add(New SampleItem(12, "こんにちは"))
    End Sub
End Class

Public Class MainClass
    Public Shared Sub Main()
        Dim fileName As String = "C:\test\test.xml"

        'シリアル化するオブジェクト
        Dim obj As New SampleClass()

        '普通にシリアル化できるようになる
        Dim serializer As New DataContractSerializer(GetType(SampleClass))
        Dim settings As New XmlWriterSettings()
        settings.Encoding = New System.Text.UTF8Encoding(False)
        Dim xw As XmlWriter = XmlWriter.Create(fileName, settings)
        serializer.WriteObject(xw, obj)
        xw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System.Runtime.Serialization;
using System.Xml;
using System.Collections;

[DataContract]
public class SampleItem
{
    [DataMember]
    public int Number;
    [DataMember]
    public string Message;

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

//KnownTypeAttributeを適用し、シリアル化の時に認識すべき型を指定する
[DataContract]
[KnownType(typeof(SampleItem))]
public class SampleClass
{
    [DataMember]
    public System.Collections.ArrayList Items;

    public SampleClass()
    {
        Items = new System.Collections.ArrayList();
        Items.Add(new SampleItem(12, "こんにちは"));
    }
}

public class MainClass
{
    public static void Main()
    {
        string fileName = @"C:\test\test.xml";

        //シリアル化するオブジェクト
        SampleClass obj = new SampleClass();

        //普通にシリアル化できるようになる
        DataContractSerializer serializer =
            new DataContractSerializer(typeof(SampleClass));
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Encoding = new System.Text.UTF8Encoding(false);
        XmlWriter xw = XmlWriter.Create(fileName, settings);
        serializer.WriteObject(xw, obj);
        xw.Close();
    }
}

上記のコードで作成されたXMLファイルの内容は、以下のようになります。

<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<Items xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:anyType i:type="SampleItem">
<Message>こんにちは</Message>
<Number>12</Number>
</d2p1:anyType>
</Items>
</SampleClass>
  • 履歴:
  • 2014/5/12 FileStreamの代わりにXmlWriterとXmlReaderを使うようにした。

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

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

この記事への評価

この記事へのコメント

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