DOBON.NET

DataContractSerializerを使って、オブジェクトのXMLシリアル化、逆シリアル化を行う

オブジェクトの内容をXMLファイルに保存(シリアル化、シリアライズ)し、復元(逆シリアル化、デシリアライズ)する方法を「オブジェクトの内容をXMLファイルに保存、復元する」で説明しました。そこではXmlSerializerクラスを使いましたが、ここでは.NET Framework 3.0以降で使用できるDataContractSerializerクラス(System.Runtime.Serialization名前空間)を使う方法を紹介します。また、XmlSerializerクラスとDataContractSerializerクラスの違いにも触れます。

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

ここでは、以下のような「TestClassクラス」のインスタンスの内容をXMLファイルに保存することを考えます。

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

    'コンストラクタ
    Public Sub New()
        Me.Number = 10
        Me.Message = "こんにちは。"
        Me.PrivateMessage = "はじめまして。"
    End Sub

    'コンストラクタ
    Public Sub New(ByVal num As Integer, _
                   ByVal msg As String, _
                   ByVal pmsg As String)
        Me.Number = num
        Me.Message = msg
        Me.PrivateMessage = pmsg
    End Sub
End Class
C#
コードを隠すコードを選択
//XMLファイルに保存するオブジェクトのためのクラス
public class TestClass
{
    //メンバ
    public int Number;
    public string Message;
    private string PrivateMessage;

    //コンストラクタ
    public TestClass()
    {
        this.Number = 10;
        this.Message = "こんにちは。";
        this.PrivateMessage = "はじめまして。";
    }

    //コンストラクタ
    public TestClass(int num, string msg, string pmsg)
    {
        this.Number = num;
        this.Message = msg;
        this.PrivateMessage = pmsg;
    }
}

まず、DataContractSerializerクラスを使うためには参照設定に「System.Runtime.Serialization.dll」が必要ですので、もしなければこれを追加してください(やり方が分からない場合は、「「○○○.dllを参照に追加します」の意味は?」をご覧ください)。

次に、TestClassクラスを書き直します。TestClassクラスには、DataContractAttribute属性を適用します。

シリアル化したいメンバ(フィールドやプロパティ)には、DataMemberAttribute属性を適用します。DataMemberAttribute属性はパブリックメンバだけでなく、プライベートメンバにも適用できます。DataMemberAttribute属性が適用されていないメンバは、シリアル化されません。

補足:DataContractSerializerクラスは、DataContractAttribute属性が適用されていない型でもシリアル化することもできます。その場合は、XmlSerializerクラスと同じように、パブリックメンバだけをシリアル化します。シリアル化できる型については、「データ コントラクト シリアライザーでサポートされる型」で説明されています。

オブジェクトの内容をシリアル化するには、DataContractSerializer.WriteObjectメソッドを使います。使い方は、XmlSerializerの場合とほぼ同じです。

以下に、TestClassオブジェクトをXMLファイルに保存するサンプルコードを示します。ここでは、TestClassクラスのNumberとPrivateMessageフィールドを保存し、Messageフィールドは保存しないようにしています。

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

'XMLファイルに保存するオブジェクトのためのクラス
<DataContract> _
Public Class TestClass
    <DataMember> _
    Public Number As Integer
    'DataMemberAttributeが無いのでシリアル化されない
    Public Message As String
    'プライベートでもDataMemberAttributeがあればシリアル化される
    <DataMember> _
    Private PrivateMessage As String

    'コンストラクタ
    Public Sub New()
        Me.Number = 10
        Me.Message = "こんにちは。"
        Me.PrivateMessage = "はじめまして。"
    End Sub

    'コンストラクタ
    Public Sub New(num As Integer, msg As String, pmsg As String)
        Me.Number = num
        Me.Message = msg
        Me.PrivateMessage = pmsg
    End Sub
End Class

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

        '保存するクラス(TestClass)のインスタンスを作成
        Dim obj As New TestClass()

        'DataContractSerializerオブジェクトを作成
        'オブジェクトの型を指定する
        Dim serializer As New DataContractSerializer(GetType(TestClass))
        'BOMが付かないUTF-8で、書き込むファイルを開く
        Dim settings As New XmlWriterSettings()
        settings.Encoding = New System.Text.UTF8Encoding(False)
        Dim xw As XmlWriter = XmlWriter.Create(fileName, settings)
        'シリアル化し、XMLファイルに保存する
        serializer.WriteObject(xw, obj)
        'ファイルを閉じる
        xw.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System.Runtime.Serialization;
using System.Xml;

//XMLファイルに保存するオブジェクトのためのクラス
[DataContract]
public class TestClass
{
    [DataMember]
    public int Number;
    //DataMemberAttributeが無いのでシリアル化されない
    public string Message;
    //プライベートでもDataMemberAttributeがあればシリアル化される
    [DataMember]
    private string PrivateMessage;

    //コンストラクタ
    public TestClass()
    {
        this.Number = 10;
        this.Message = "こんにちは。";
        this.PrivateMessage = "はじめまして。";
    }

    //コンストラクタ
    public TestClass(int num, string msg, string pmsg)
    {
        this.Number = num;
        this.Message = msg;
        this.PrivateMessage = pmsg;
    }
}

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

        //保存するクラス(TestClass)のインスタンスを作成
        TestClass obj = new TestClass();

        //DataContractSerializerオブジェクトを作成
        //オブジェクトの型を指定する
        DataContractSerializer serializer =
            new DataContractSerializer(typeof(TestClass));
        //BOMが付かないUTF-8で、書き込むファイルを開く
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Encoding = new System.Text.UTF8Encoding(false);
        XmlWriter xw = XmlWriter.Create(fileName, settings);
        //シリアル化し、XMLファイルに保存する
        serializer.WriteObject(xw, obj);
        //ファイルを閉じる
        xw.Close();
    }
}

保存されるXMLファイルの中身は、以下のようになります(見やすいように改行を入れていますが、実際は1行です)。

<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<Number>10</Number>
<PrivateMessage>はじめまして。</PrivateMessage>
</TestClass>

補足:上のコードではXmlWriterを使用していますが、代わりにFileStreamのみを使用して書き込みをした時は、XML宣言(上の例では、「<?xml version="1.0" encoding="utf-8"?>」)が省略され、UTF-8で書き込まれます。また、XMLルート要素の名前空間が「http://schemas.datacontract.org/2004/07/(アプリケーションの名前)」のように、アプリケーションの名前に基づいた文字列になります。

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

逆シリアル化するには、DataContractSerializer.ReadObjectメソッドを使います。

上記の方法で保存されたXMLファイルを復元するサンプルコードを以下に示します。TestClassクラスは先ほどと全く同じです。

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

'XMLファイルに保存するオブジェクトのためのクラス
<DataContract> _
Public Class TestClass
    <DataMember> _
    Public Number As Integer
    Public Message As String
    <DataMember> _
    Private PrivateMessage As String

    Public Sub New()
        Me.Number = 0
        Me.Message = "こんにちは。"
        Me.PrivateMessage = "はじめまして。"
    End Sub

    Public Sub New(num As Integer, msg As String, pmsg As String)
        Me.Number = num
        Me.Message = msg
        Me.PrivateMessage = pmsg
    End Sub
End Class

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

        'DataContractSerializerオブジェクトを作成
        Dim serializer As New DataContractSerializer(GetType(TestClass))
        '読み込むファイルを開く
        Dim xr As XmlReader = XmlReader.Create(fileName)
        'XMLファイルから読み込み、逆シリアル化する
        Dim obj As TestClass = DirectCast(serializer.ReadObject(xr), TestClass)
        'ファイルを閉じる
        xr.Close()
    End Sub
End Class
C#
コードを隠すコードを選択
using System.Runtime.Serialization;
using System.Xml;

//XMLファイルに保存するオブジェクトのためのクラス
[DataContract]
public class TestClass
{
    [DataMember]
    public int Number;
    public string Message;
    [DataMember]
    private string PrivateMessage;

    public TestClass()
    {
        this.Number = 0;
        this.Message = "こんにちは。";
        this.PrivateMessage = "はじめまして。";
    }

    public TestClass(int num, string msg, string pmsg)
    {
        this.Number = num;
        this.Message = msg;
        this.PrivateMessage = pmsg;
    }
}

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

        //DataContractSerializerオブジェクトを作成
        DataContractSerializer serializer =
            new DataContractSerializer(typeof(TestClass));
        //読み込むファイルを開く
        XmlReader xr = XmlReader.Create(fileName);
        //XMLファイルから読み込み、逆シリアル化する
        TestClass obj = (TestClass)serializer.ReadObject(xr);
        //ファイルを閉じる
        xr.Close();
    }
}

復元されたTestClassオブジェクトのMessageプロパティは、Nothing(C#では、null)になります。なぜなら、DataContractSerializerは逆シリアル化する時にTestClassクラスのコンストラクタを呼び出さないからです。

シリアル化、逆シリアル化されるデータの順番

DataContractAttributeでは、シリアル化されたXMLのデータの順番がとても重要です。これを変更すると、正しく逆シリアル化できなくなります。

例えば上の例で、XMLファイルのNumberとPrivateMessageの順番を入れ替えて逆シリアル化すると(下記のXMLを逆シリアル化すると)、Numberが0になり、正しく逆シリアル化されません。

<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<PrivateMessage>はじめまして。</PrivateMessage>
<Number>10</Number>
</TestClass>

これは、本来の順番がNumber、PrivateMessageであるため、PrivateMessageの後のNumberが無視されるためです。

この順番には法則があります。順番は、まず基本型のメンバが先で、次にアルファベット順です。

この順番を変更する方法があります。メンバにDataMemberAttribute.Orderプロパティを指定すると、これが指定されていないメンバの後の順番になります。そして、Orderプロパティに指定された数字の順番に並びます。Orderの値が同じ時は、アルファベット順です。詳しくは、「データ メンバーの順序」に説明があります。

上の例で、PrivateMessage、Numberの順番にするには、TestClassクラスを以下のように変更します。

VB.NET
コードを隠すコードを選択
<DataContract()> _
Public Class TestClass
    <DataMember(Order:=1)> _
    Public Number As Integer
    <DataMember(Order:=0)> _
    Private PrivateMessage As String
End Class
C#
コードを隠すコードを選択
[DataContract]
public class TestClass
{
    [DataMember(Order = 1)]
    public int Number;
    [DataMember(Order = 0)]
    private string PrivateMessage;
}

要素名を変更する

デフォルトでは、XMLの要素名にはメンバの名前や型の名前が使われます。これを変更するには、DataMemberAttributeやDataContractAttributeのNameプロパティを使います。

例えばTestClassクラスを以下のように書き換えたとします。

VB.NET
コードを隠すコードを選択
<DataContract(Name:="サンプル")> _
Public Class TestClass
    <DataMember(Name:="数字")> _
    Public Number As Integer
    <DataMember(Name:="文字列")> _
    Public Message As String
End Class
C#
コードを隠すコードを選択
[DataContract(Name = "サンプル")]
public class TestClass
{
    [DataMember(Name = "数字")]
    public int Number;
    [DataMember(Name = "文字列")]
    public string Message;
}

シリアル化されたXMLは以下のようになります。

<?xml version="1.0" encoding="utf-8"?>
<サンプル xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<数字>10</数字>
<文字列>こんにちは。</文字列>
</サンプル>

名前空間を変更する

今までの例では、XMLルート要素の名前空間が「http://schemas.datacontract.org/2004/07/」となりました。名前空間を変更するには、DataContractAttribute.Namespaceプロパティを指定します。名前空間を使わないようにするには、Namespaceプロパティに空白文字列を指定します。なお、名前空間が同じでないと逆シリアル化できません。

VB.NET
コードを隠すコードを選択
'名前空間を使わないようにする
<DataContract([Namespace]:="")> _
Public Class TestClass
    <DataMember()> _
    Public Number As Integer
    <DataMember()> _
    Public Message As String
End Class
C#
コードを隠すコードを選択
//名前空間を使わないようにする
[DataContract(Namespace = "")]
public class TestClass
{
    [DataMember]
    public int Number;
    [DataMember]
    public string Message;
}

このようにすると、以下のようにシリアル化されます。

<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Message>こんにちは。</Message>
<Number>10</Number>
</TestClass>

シリアル化する型に今後メンバが追加される可能性がある時

例えばアプリケーションの設定をシリアル化しており、バージョンアップで設定が追加される可能性がある(つまりシリアル化する型にメンバが追加される可能性がある)場合は、IExtensibleDataObjectインターフェイスを実装してください。詳しくは、「DataContractSerializerで、上位下位互換性を保ってシリアル化、逆シリアル化できるようにする」で説明します。

XmlSerializerとDataContractSerializerの比較

以下にXmlSerializerとDataContractSerializerの違いを列挙します。すでに述べた事柄も含まれています。DataContractSerializerの場合は、基本的にDataContractAttribute属性が適用された型に対するシリアル化について述べています。

  • DataContractSerializerは.NET Framework 3.0で追加され、Wcfで使用されています。XmlSerializerはすべてのバージョンの.NET Frameworkで使用できます。
  • 速度の比較は、「XmlSerializer vs DataContractSerializer」によると、DataContractSerializerの方が約10%速いということです。パフォーマンスはDataContractSerializerの方が優れているようです。
  • MSDNの「DataMemberAttribute クラス」によると、DataContractSerializerのデータコントラクトモデルは「opt-in」モデルです。つまり、シリアル化するメンバを明示的に指定する必要があり、そのメンバはパブリックだけでなく、プライベートでも構いません。XmlSerializerは「opt-out」であり、何も指定しないと、すべてのパブリックなメンバがシリアル化されます。
  • DataContractSerializerはDataContractAttribute属性が適用された型だけでなく、様々な型(MSDNの「データ コントラクト シリアライザーでサポートされる型」に説明があります)をシリアル化することができます。XmlSerializerがシリアル化できる型を含め、HashtableやDictionaryなどもシリアル化できます。(XmlSerializerでHashtableやDictionaryなどもシリアル化する方法は、「HashtableやDictionaryオブジェクトをXMLシリアル化する」で説明しています。)
  • オブジェクトの内容をXMLファイルに保存、復元する」で説明したように、XmlSerializerはXmlRootAttribute、XmlElementAttribute、XmlTextAttribute、XmlTextAttributeなどの属性を使った細かい設定が可能です。DataContractSerializerではそこまではできません。
  • DataContractSerializerがシリアル化する型には、パラメータを持たないコンストラクタが必要なく、逆シリアル化する時にコンストラクタを呼び出しません。XmlSerializerはパラメータを持たないコンストラクタが必要で、逆シリアル化の時にこのコンストラクタが呼び出されます。
  • DataContractSerializerは、逆シリアル化するXML内のデータの順番が非常に重要で、順番を変えると正しく逆シリアル化できません。XmlSerializerの場合は、基本的には、XMLを書き換えてデータの順番を変えても逆シリアル化できます。ただし派生クラスの場合は、基本クラスのメンバを前にしないと、基本クラスのメンバは正しく逆シリアル化されません。
  • DataContractSerializerは、デフォルトで、アプリケーション名に由来した名前空間を付けます。XmlSerializerはデフォルトで名前空間を付けません。
  • XmlSerializerもDataContractSerializerも、バージョンアップでシリアル化する型のメンバが増減されたとしても、大体は互換性が保たれます。DataContractSerializerの場合はさらに、ラウンドトリップを有効にすると、新しいバージョンでシリアル化したものを古いバージョンで逆シリアル化したとしても、新しいバージョンで追加されたメンバの情報を保持できます。詳しくは、「DataContractSerializerで、上位下位互換性を保ってシリアル化、逆シリアル化できるようにする」をご覧ください。
  • 履歴:
  • 2014/5/12 FileStreamの代わりにXmlWriterとXmlReaderを使うようにした。

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

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

この記事への評価

この記事へのコメント

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