オブジェクトの内容をXMLファイルに保存(シリアル化、シリアライズ)し、復元(逆シリアル化、デシリアライズ)する方法を「オブジェクトの内容をXMLファイルに保存、復元する」で説明しました。そこではXmlSerializerクラスを使いましたが、ここでは.NET Framework 3.0以降で使用できるDataContractSerializerクラス(System.Runtime.Serialization名前空間)を使う方法を紹介します。また、XmlSerializerクラスとDataContractSerializerクラスの違いにも触れます。
ここでは、以下のような「TestClassクラス」のインスタンスの内容をXMLファイルに保存することを考えます。
'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
//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フィールドは保存しないようにしています。
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
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/(アプリケーションの名前)」のように、アプリケーションの名前に基づいた文字列になります。
逆シリアル化するには、DataContractSerializer.ReadObjectメソッドを使います。
上記の方法で保存されたXMLファイルを復元するサンプルコードを以下に示します。TestClassクラスは先ほどと全く同じです。
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
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クラスを以下のように変更します。
<DataContract()> _ Public Class TestClass <DataMember(Order:=1)> _ Public Number As Integer <DataMember(Order:=0)> _ Private PrivateMessage As String End Class
[DataContract] public class TestClass { [DataMember(Order = 1)] public int Number; [DataMember(Order = 0)] private string PrivateMessage; }
デフォルトでは、XMLの要素名にはメンバの名前や型の名前が使われます。これを変更するには、DataMemberAttributeやDataContractAttributeのNameプロパティを使います。
例えばTestClassクラスを以下のように書き換えたとします。
<DataContract(Name:="サンプル")> _ Public Class TestClass <DataMember(Name:="数字")> _ Public Number As Integer <DataMember(Name:="文字列")> _ Public Message As String End Class
[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プロパティに空白文字列を指定します。なお、名前空間が同じでないと逆シリアル化できません。
'名前空間を使わないようにする <DataContract([Namespace]:="")> _ Public Class TestClass <DataMember()> _ Public Number As Integer <DataMember()> _ Public Message As String End Class
//名前空間を使わないようにする [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の違いを列挙します。すでに述べた事柄も含まれています。DataContractSerializerの場合は、基本的にDataContractAttribute属性が適用された型に対するシリアル化について述べています。