オブジェクトの内容をXMLファイルにして保存し、そのXMLファイルから元のオブジェクトに復元できると色々と便利です。例えば、INIファイルの代わりとして、設定を保存、復元したい時などに有用です。(設定を保存する場合は、「アプリケーションの設定を保存する」も参考にしてください。)
これを行うには、XmlSerializerクラス(System.Xml.Serialization名前空間)を使うと簡単です。XMLファイルに書き込む時はSerializeメソッドを使ってオブジェクトをXMLシリアル化(シリアライズ)し、XMLファイルを読み込む時はDeserializeメソッドを使ってXML逆シリアル化(デシリアライズ)をします。
次のコードでは、SampleClassオブジェクトの内容をXMLファイル(C:\test\sample.xml)に保存しています。ここではファイルの書き込みにStreamWriterクラスを使っていますが、StreamWriterの使い方が分からないという場合はまず「文字コードを指定してテキストファイルに書き込む」をご覧ください。
'XMLファイルに保存するオブジェクトのためのクラス Public Class SampleClass Public Number As Integer Public Message As String End Class Class MainClass 'SampleClassオブジェクトをXMLファイルに保存する Public Shared Sub Main() '保存先のファイル名 Dim fileName As String = "C:\test\sample.xml" '保存するクラス(SampleClass)のインスタンスを作成 Dim obj As New SampleClass() obj.Message = "テストです。" obj.Number = 123 'XmlSerializerオブジェクトを作成 'オブジェクトの型を指定する Dim serializer As New System.Xml.Serialization.XmlSerializer( _ GetType(SampleClass)) '書き込むファイルを開く(UTF-8 BOM無し) Dim sw As New System.IO.StreamWriter( _ fileName, False, New System.Text.UTF8Encoding(False)) 'シリアル化し、XMLファイルに保存する serializer.Serialize(sw, obj) 'ファイルを閉じる sw.Close() End Sub End Class
//XMLファイルに保存するオブジェクトのためのクラス public class SampleClass { public int Number; public string Message; } class MainClass { //SampleClassオブジェクトをXMLファイルに保存する public static void Main() { //保存先のファイル名 string fileName = @"C:\test\sample.xml"; //保存するクラス(SampleClass)のインスタンスを作成 SampleClass obj = new SampleClass(); obj.Message = "テストです。"; obj.Number = 123; //XmlSerializerオブジェクトを作成 //オブジェクトの型を指定する System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass)); //書き込むファイルを開く(UTF-8 BOM無し) System.IO.StreamWriter sw = new System.IO.StreamWriter( fileName, false, new System.Text.UTF8Encoding(false)); //シリアル化し、XMLファイルに保存する serializer.Serialize(sw, obj); //ファイルを閉じる sw.Close(); } }
補足:インスタンスをシリアル化できるクラスには通常SerializableAttribute属性を適用しますが、上記の例のように、XmlSerializerではそうしなくてもシリアル化できます。
補足:以前紹介していたサンプルコードは、StreamWriterの代わりにFileStreamを使用していました。FileStreamだけを使用すると、作成されるXMLファイルのProcessingInstructionからencoding属性が省略され(つまり、「<?xml version="1.0" encoding="utf-8"?>」が「<?xml version="1.0"?>」に)、UTF-8で書き込まれます。ただし、UTF-8で書き込まれないというコメントを幾つかいただきましたので、環境等によってはそうならない可能性もあるのかもしれません。
上記の例により作成された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>123</Number> <Message>テストです。</Message> </SampleClass>
この方法によりXMLファイルに書き込まれるメンバは、パブリックメンバであるフィールドとプロパティだけです。さらに、読み込み、あるいは書き込みしか許可されていないプロパティは、パブリックメンバであっても書き込まれません。また、シリアル化するオブジェクトのクラスには、規定のコンストラクタ(パラメータを持たないコンストラクタ)が必要です。
次に、上記のコードにより保存されたXMLファイルから、SampleClassオブジェクトの内容を復元するコードを示します。ファイルの読み込みにはStreamReaderクラスを使っていますが、この詳細は「文字コードを指定してテキストファイルを読み込む」をご覧ください。
'XMLファイルに保存するオブジェクトのためのクラス Public Class SampleClass Public Number As Integer Public Message As String End Class Class MainClass 'XMLファイルをSampleClassオブジェクトに復元する Public Shared Sub Main() '保存元のファイル名 Dim fileName As String = "C:\test\sample.xml" 'XmlSerializerオブジェクトを作成 Dim serializer As New System.Xml.Serialization.XmlSerializer( _ GetType(SampleClass)) '読み込むファイルを開く Dim sr As New System.IO.StreamReader( _ fileName, New System.Text.UTF8Encoding(False)) 'XMLファイルから読み込み、逆シリアル化する Dim obj As SampleClass = _ DirectCast(serializer.Deserialize(sr), SampleClass) 'ファイルを閉じる sr.Close() End Sub End Class
//XMLファイルに保存するオブジェクトのためのクラス public class SampleClass { public int Number; public string Message; } class MainClass { //XMLファイルをSampleClassオブジェクトに復元する public static void Main() { //保存元のファイル名 string fileName = @"C:\test\sample.xml"; //XmlSerializerオブジェクトを作成 System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass)); //読み込むファイルを開く System.IO.StreamReader sr = new System.IO.StreamReader( fileName, new System.Text.UTF8Encoding(false)); //XMLファイルから読み込み、逆シリアル化する SampleClass obj = (SampleClass)serializer.Deserialize(sr); //ファイルを閉じる sr.Close(); } }
シリアル化したいオブジェクトが配列やList<T>などのジェネリックコレクションであっても、やり方は全く同じです。
ただし、ArrayList、Hashtable、Dictionaryなどは、そのままではうまく行きません。これらに関しては、「Object配列やArrayListをXMLシリアル化する」や「HashtableやDictionaryオブジェクトをXMLシリアル化する」をご覧下さい。
以下に、SampleClassの配列をシリアル化、逆シリアル化する例を示します。
'次のクラスが宣言されているものとする 'Public Class SampleClass ' Public Number As Integer ' Public Message As String 'End Class '保存する配列を作成 Dim ary As SampleClass() = New SampleClass(1) {} For i As Integer = 0 To ary.Length - 1 ary(i) = New SampleClass() ary(i).Number = i ary(i).Message = i.ToString() & "です。" Next 'XMLファイルに保存する Dim serializer1 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)) serializer1.Serialize(sw, ary) sw.Close() '保存した内容を復元する Dim serializer2 As New System.Xml.Serialization.XmlSerializer( _ GetType(SampleClass())) Dim sr As New System.IO.StreamReader( _ "C:\test\sample.xml", New System.Text.UTF8Encoding(False)) Dim loadAry As SampleClass() loadAry = DirectCast(serializer2.Deserialize(sr), SampleClass()) sr.Close()
////次のクラスが宣言されているものとする //public class SampleClass //{ // public int Number; // public string Message; //} //保存する配列を作成 SampleClass[] ary = new SampleClass[2]; for (int i = 0; i < ary.Length; i++) { ary[i] = new SampleClass(); ary[i].Number = i; ary[i].Message = i.ToString() + "です。"; } //XMLファイルに保存する System.Xml.Serialization.XmlSerializer serializer1 = 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)); serializer1.Serialize(sw, ary); sw.Close(); //保存した内容を復元する System.Xml.Serialization.XmlSerializer serializer2 = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass[])); System.IO.StreamReader sr = new System.IO.StreamReader( @"C:\test\sample.xml", new System.Text.UTF8Encoding(false)); SampleClass[] loadAry; loadAry = (SampleClass[])serializer2.Deserialize(sr); sr.Close();
保存されたXMLファイルの内容は次のようになります。
<?xml version="1.0" encoding="utf-8"?> <ArrayOfSampleClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SampleClass> <Number>0</Number> <Message>0です。</Message> </SampleClass> <SampleClass> <Number>1</Number> <Message>1です。</Message> </SampleClass> </ArrayOfSampleClass>
ここからは、シリアル化した時にどのようにXMLに記述されるかを制御したいという方のための、より高度な話題になります。よって、必要ない方はお読み頂く必要はありません。
パブリックメンバであってもXMLファイルに書き込みたくないケースも考えられます。このような時は、書き込みたくないメンバにXmlIgnoreAttribute属性をつけます。先のSampleClassクラスを次のように書き換えることにより、SampleClassオブジェクトのMessageフィールドの値がXMLファイルに書き込まれなくなります。
'XMLファイルに保存するオブジェクトのためのクラス Public Class SampleClass Public Number As Integer 'このメンバの値はシリアル化しないようにする <System.Xml.Serialization.XmlIgnore()> _ Public Message As String End Class
//XMLファイルに保存するオブジェクトのためのクラス public class SampleClass { public int Number; //このメンバの値はシリアル化しないようにする [System.Xml.Serialization.XmlIgnore] public string Message; }
今までの例では、クラスの名前(SampleClass)とメンバの名前(NumberとMessage)がそのままXMLファイルの要素名に利用されていました。しかし、この要素名を別の名前に変えたいケースもあるでしょう。そのような時は、XmlRootAttribute及びXmlElementAttributeを指定します。
先のSampleClassクラスを次のように書き換え、"SampleClass"の代わりに"サンプル"、"Number"と"Message"の代わりに"数字"と"文字列"と、XMLファイルに出力するようにしています。
'XMLファイルに保存するオブジェクトのためのクラス <System.Xml.Serialization.XmlRoot("サンプル")> _ Public Class SampleClass <System.Xml.Serialization.XmlElement("数字")> _ Public Number As Integer <System.Xml.Serialization.XmlElement("文字列")> _ Public Message As String End Class
//XMLファイルに保存するオブジェクトのためのクラス [System.Xml.Serialization.XmlRoot("サンプル")] public class SampleClass { [System.Xml.Serialization.XmlElement("数字")] public int Number; [System.Xml.Serialization.XmlElement("文字列")] public string Message; }
出力されるXMLファイルの内容は次のようになります。
<?xml version="1.0" encoding="utf-8"?> <サンプル xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <数字>123</数字> <文字列>テストです。</文字列> </サンプル>
メンバをXML属性として保存するにはXmlAttributeAttributeを、XMLテキストとして保存するにはXmlTextAttributeを使います。
'XMLファイルに保存するオブジェクトのためのクラス <System.Xml.Serialization.XmlRoot("サンプル")> _ Public Class SampleClass <System.Xml.Serialization.XmlText()> _ Public Number As Integer <System.Xml.Serialization.XmlAttribute("文字列")> _ Public Message As String End Class
//XMLファイルに保存するオブジェクトのためのクラス [System.Xml.Serialization.XmlRoot("サンプル")] public class SampleClass { [System.Xml.Serialization.XmlText] public int Number; [System.Xml.Serialization.XmlAttribute("文字列")] public string Message; }
結果は次のようになります。
<?xml version="1.0" encoding="utf-8"?> <サンプル xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 文字列="テストです。">123</サンプル>
XmlRootAttributeとXmlAttributeAttributeのNamespaceプロパティによって、名前空間を指定することができます。
<System.Xml.Serialization.XmlRoot([Namespace]:="https://dobon.net")> _ Public Class SampleClass 'パブリックメンバ Public Number As Integer Public Message As String End Class
[System.Xml.Serialization.XmlRoot(Namespace = "https://dobon.net")] public class SampleClass { //パブリックメンバ public int Number; public string Message; }
<?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" xmlns="https://dobon.net"> <Number>123</Number> <Message>テストです。</Message> </SampleClass>
例えば、以下のように配列(あるいは、List<T>などのジェネリックコレクションでも同じです)のメンバを持つクラスのインスタンスをシリアル化したとします。
Public Class SampleClass Public Samples As SampleItem() = New SampleItem() _ {New SampleItem(), New SampleItem()} End Class Public Class SampleItem Public Number As Integer = 1 Public Message As String = "いち" End Class
public class SampleClass { public SampleItem[] Samples = new SampleItem[] { new SampleItem(), new SampleItem() }; } public class SampleItem { public int Number = 1; public string Message = "いち"; }
この時の結果は、以下のようになります。
<?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"> <Samples> <SampleItem> <Number>1</Number> <Message>いち</Message> </SampleItem> <SampleItem> <Number>1</Number> <Message>いち</Message> </SampleItem> </Samples> </SampleClass>
もし配列メンバの要素名(上の例では、"Samples")を変更したいのであれば、XmlArrayAttribute属性を使います。また、配列要素の要素名(上の例では、"SampleItem")を変更したいのであれば、XmlArrayItemAttribute属性を使います。
これらの属性を使って、先ほどのSampleClassクラスを次のように書き換えたとします。
Public Class SampleClass <System.Xml.Serialization.XmlArray("リスト")> _ <System.Xml.Serialization.XmlArrayItem("サンプル")> _ Public Samples As SampleItem() = New SampleItem() _ {New SampleItem(), New SampleItem()} End Class
public class SampleClass { [System.Xml.Serialization.XmlArray("リスト")] [System.Xml.Serialization.XmlArrayItem("サンプル")] public SampleItem[] Samples = new SampleItem[] { new SampleItem(), new SampleItem() }; }
すると、シリアル化の結果は次のようになります。
<?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>1</Number> <Message>いち</Message> </サンプル> <サンプル> <Number>1</Number> <Message>いち</Message> </サンプル> </リスト> </SampleClass>
ちなみに、XmlArrayAttributeとXmlArrayItemAttributeの代わりにXmlElementAttributeを使った時は、次のようになります。
Public Class SampleClass <System.Xml.Serialization.XmlElement("サンプル")> _ Public Samples As SampleItem() = New SampleItem() _ {New SampleItem(), New SampleItem()} End Class
public class SampleClass { [System.Xml.Serialization.XmlElement("サンプル")] public SampleItem[] Samples = new SampleItem[] { new SampleItem(), new SampleItem() }; }
<?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>1</Number> <Message>いち</Message> </サンプル> <サンプル> <Number>1</Number> <Message>いち</Message> </サンプル> </SampleClass>
XmlArrayAttributeとXmlArrayItemAttribute使わずに、SampleItemクラスの方にXmlRootAttribute属性を使えば配列要素の要素名(SampleItem)を変更できそうですが、実際にはできません。
Public Class SampleClass Public Samples As SampleItem() = New SampleItem() _ {New SampleItem(), New SampleItem()} End Class <System.Xml.Serialization.XmlRoot("サンプル")> _ Public Class SampleItem Public Number As Integer = 1 Public Message As String = "いち" End Class
public class SampleClass { public SampleItem[] Samples = new SampleItem[] { new SampleItem(), new SampleItem() }; } [System.Xml.Serialization.XmlRoot("サンプル")] public class SampleItem { public int Number = 1; public string Message = "いち"; }
<?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"> <Samples> <SampleItem> <Number>1</Number> <Message>いち</Message> </SampleItem> <SampleItem> <Number>1</Number> <Message>いち</Message> </SampleItem> </Samples> </SampleClass>
しかし、XmlRootAttributeの代わりにXmlTypeAttributeを使えば、できます。
Public Class SampleClass Public Samples As SampleItem() = New SampleItem() _ {New SampleItem(), New SampleItem()} End Class <System.Xml.Serialization.XmlType("サンプル")> _ Public Class SampleItem Public Number As Integer = 1 Public Message As String = "いち" End Class
public class SampleClass { public SampleItem[] Samples = new SampleItem[] { new SampleItem(), new SampleItem() }; } [System.Xml.Serialization.XmlType("サンプル")] public class SampleItem { public int Number = 1; public string Message = "いち"; }
<?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"> <Samples> <サンプル> <Number>1</Number> <Message>いち</Message> </サンプル> <サンプル> <Number>1</Number> <Message>いち</Message> </サンプル> </Samples> </SampleClass>