「オブジェクトの内容をファイルに保存、復元する」で説明している方法でHashtableやDictionaryオブジェクトをXMLファイルに保存(シリアル化)しようとすると、例外NotSupportedException(型 System.Collections.Hashtable は IDictionary を実装するため、サポートされません)が発生します。ここではこの回避法を幾つか紹介します。
なおここでは「オブジェクトの内容をファイルに保存、復元する」で説明している事柄については一切説明しませんので、基本的な部分が分からないという場合は、まずそちらをご覧ください。
.NET Framework 3.0で追加されたDataContractSerializerクラスを使うと、HashtableやDictionaryをシリアル化できます。DataContractSerializerの使い方は、「DataContractSerializerを使って、オブジェクトのXMLシリアル化、逆シリアル化を行う」で説明しています。
あまりスマートなやり方ではありませんが、HashtableやDictionaryをシリアル化できる型に変換する方法が考えられます。
以下の例では、HashtableをDictionaryEntry構造体の配列に変換してシリアル化し、逆シリアル化する時は、逆の変換で元に戻しています。XmlSerializeメソッドでHashtableをXMLファイルに保存し、XmlDeserializeメソッドで復元できます。
Imports System.Xml.Serialization Imports System.Collections Imports System.IO Imports System.Text Public Class Program ''' <summary> ''' HashtableをDictionaryEntryの配列に変換する ''' </summary> ''' <param name="ht">変換するHashtable</param> ''' <returns>変換されたDictionaryEntry配列</returns> Public Shared Function ConvertHashtableToArray(ht As Hashtable) _ As DictionaryEntry() Dim entries As DictionaryEntry() = New DictionaryEntry(ht.Count - 1) {} Dim entryIndex As Integer = 0 For Each de As DictionaryEntry In ht entries(entryIndex) = de entryIndex += 1 Next Return entries End Function ''' <summary> ''' DictionaryEntry配列をHashtableに変換する ''' </summary> ''' <param name="ary">変換するDictionaryEntry配列</param> ''' <returns>変換されたHashtable</returns> Public Shared Function ConvertArrayToHashtable(ary As DictionaryEntry()) _ As Hashtable Dim ht As New Hashtable(ary.Length) For Each de As DictionaryEntry In ary ht.Add(de.Key, de.Value) Next Return ht End Function ''' <summary> ''' HashtableをXMLファイルに保存する ''' </summary> ''' <param name="fileName">保存先のファイル名</param> ''' <param name="ht">保存するHashtable</param> Public Shared Sub XmlSerialize(fileName As String, ht As Hashtable) 'シリアル化できる型に変換 Dim obj As DictionaryEntry() = ConvertHashtableToArray(ht) 'XMLファイルに保存 Dim serializer As New XmlSerializer(GetType(DictionaryEntry())) Dim sw As New StreamWriter(fileName, False, New UTF8Encoding(False)) serializer.Serialize(sw, obj) sw.Close() End Sub ''' <summary> ''' シリアル化されたXMLファイルからHashtableを復元する ''' </summary> ''' <param name="fileName">復元するXMLファイル名</param> ''' <returns>復元されたHashtable</returns> Public Shared Function XmlDeserialize(fileName As String) As Hashtable 'XMLファイルから復元 Dim serializer As New XmlSerializer(GetType(DictionaryEntry())) Dim sr As New StreamReader(fileName, New UTF8Encoding(False)) Dim obj As DictionaryEntry() = _ DirectCast(serializer.Deserialize(sr), DictionaryEntry()) sr.Close() 'Hashtableに戻す Dim ht As Hashtable = ConvertArrayToHashtable(obj) Return ht End Function 'エントリポイント Public Shared Sub Main(args As String()) '保存先のファイル名 Dim fileName As String = "C:\test\sample.xml" 'シリアル化するHashtableを作成 Dim ht As New Hashtable() ht.Add(1, "いち") ht.Add(2, "に") ht.Add(3, "さん") 'HashtableをXMLファイルに保存する XmlSerialize(fileName, ht) 'HashtableをXMLファイルから復元する Dim ht2 As Hashtable = XmlDeserialize(fileName) End Sub End Class
using System.Xml.Serialization; using System.Collections; using System.IO; using System.Text; public class Program { /// <summary> /// HashtableをDictionaryEntryの配列に変換する /// </summary> /// <param name="ht">変換するHashtable</param> /// <returns>変換されたDictionaryEntry配列</returns> public static DictionaryEntry[] ConvertHashtableToArray(Hashtable ht) { DictionaryEntry[] entries = new DictionaryEntry[ht.Count]; int entryIndex = 0; foreach (DictionaryEntry de in ht) { entries[entryIndex] = de; entryIndex++; } return entries; } /// <summary> /// DictionaryEntry配列をHashtableに変換する /// </summary> /// <param name="ary">変換するDictionaryEntry配列</param> /// <returns>変換されたHashtable</returns> public static Hashtable ConvertArrayToHashtable(DictionaryEntry[] ary) { Hashtable ht = new Hashtable(ary.Length); foreach (DictionaryEntry de in ary) { ht.Add(de.Key, de.Value); } return ht; } /// <summary> /// HashtableをXMLファイルに保存する /// </summary> /// <param name="fileName">保存先のファイル名</param> /// <param name="ht">保存するHashtable</param> public static void XmlSerialize(string fileName, Hashtable ht) { //シリアル化できる型に変換 DictionaryEntry[] obj = ConvertHashtableToArray(ht); //XMLファイルに保存 XmlSerializer serializer = new XmlSerializer(typeof(DictionaryEntry[])); StreamWriter sw = new StreamWriter(fileName, false, new UTF8Encoding(false)); serializer.Serialize(sw, obj); sw.Close(); } /// <summary> /// シリアル化されたXMLファイルからHashtableを復元する /// </summary> /// <param name="fileName">復元するXMLファイル名</param> /// <returns>復元されたHashtable</returns> public static Hashtable XmlDeserialize(string fileName) { //XMLファイルから復元 XmlSerializer serializer = new XmlSerializer(typeof(DictionaryEntry[])); StreamReader sr = new StreamReader(fileName, new UTF8Encoding(false)); DictionaryEntry[] obj = (DictionaryEntry[])serializer.Deserialize(sr); sr.Close(); //Hashtableに戻す Hashtable ht = ConvertArrayToHashtable(obj); return ht; } //エントリポイント public static void Main(string[] args) { //保存先のファイル名 string fileName = @"C:\test\sample.xml"; //シリアル化するHashtableを作成 Hashtable ht = new Hashtable(); ht.Add(1, "いち"); ht.Add(2, "に"); ht.Add(3, "さん"); //HashtableをXMLファイルに保存する XmlSerialize(fileName, ht); //HashtableをXMLファイルから復元する Hashtable ht2 = XmlDeserialize(fileName); } }
保存されるXMLファイルの内容は、以下のようになります。
<?xml version="1.0" encoding="utf-8"?> <ArrayOfDictionaryEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <DictionaryEntry> <Key xsi:type="xsd:int">3</Key> <Value xsi:type="xsd:string">さん</Value> </DictionaryEntry> <DictionaryEntry> <Key xsi:type="xsd:int">2</Key> <Value xsi:type="xsd:string">に</Value> </DictionaryEntry> <DictionaryEntry> <Key xsi:type="xsd:int">1</Key> <Value xsi:type="xsd:string">いち</Value> </DictionaryEntry> </ArrayOfDictionaryEntry>
Dictionaryの場合はKeyValuePair構造体のコレクションに変換してシリアル化したいところですが、KeyValuePairのプロパティは読み取り専用ですので、シリアル化できません。そこで、KeyValuePairに代わるシリアル化できる型を自分で用意します。
下の例では、Hashtebleの例と同様に、XmlSerializeメソッドでXMLファイルに保存し、XmlDeserializeメソッドで復元しています。
Imports System.Xml.Serialization Imports System.Collections.Generic Imports System.IO Imports System.Text Public Class Program ''' <summary> ''' シリアル化できる、KeyValuePairに代わる構造体 ''' </summary> ''' <typeparam name="TKey">Keyの型</typeparam> ''' <typeparam name="TValue">Valueの型</typeparam> <Serializable> _ Public Structure KeyAndValue(Of TKey, TValue) Public Key As TKey Public Value As TValue Public Sub New(pair As KeyValuePair(Of TKey, TValue)) Key = pair.Key Value = pair.Value End Sub End Structure ''' <summary> ''' DictionaryをKeyAndValueのListに変換する ''' </summary> ''' <typeparam name="TKey">Dictionaryのキーの型</typeparam> ''' <typeparam name="TValue">Dictionaryの値の型</typeparam> ''' <param name="dic">変換するDictionary</param> ''' <returns>変換されたKeyAndValueのList</returns> Public Shared Function ConvertDictionaryToList(Of TKey, TValue)( _ dic As Dictionary(Of TKey, TValue)) _ As List(Of KeyAndValue(Of TKey, TValue)) Dim lst As New List(Of KeyAndValue(Of TKey, TValue))() For Each pair As KeyValuePair(Of TKey, TValue) In dic lst.Add(New KeyAndValue(Of TKey, TValue)(pair)) Next Return lst End Function ''' <summary> ''' KeyAndValueのListをDictionaryに変換する ''' </summary> ''' <typeparam name="TKey">KeyAndValueのKeyの型</typeparam> ''' <typeparam name="TValue">KeyAndValueのValueの型</typeparam> ''' <param name="lst">変換するKeyAndValueのList</param> ''' <returns>変換されたDictionary</returns> Public Shared Function ConvertListToDictionary(Of TKey, TValue)( _ lst As List(Of KeyAndValue(Of TKey, TValue))) _ As Dictionary(Of TKey, TValue) Dim dic As New Dictionary(Of TKey, TValue)() For Each pair As KeyAndValue(Of TKey, TValue) In lst dic.Add(pair.Key, pair.Value) Next Return dic End Function ''' <summary> ''' DictionaryをXMLファイルに保存する ''' </summary> ''' <typeparam name="TKey">Dictionaryのキーの型</typeparam> ''' <typeparam name="TValue">Dictionaryの値の型</typeparam> ''' <param name="fileName">保存先のXMLファイル名</param> ''' <param name="dic">保存するDictionary</param> Public Shared Sub XmlSerialize(Of TKey, TValue)( _ fileName As String, dic As Dictionary(Of TKey, TValue)) 'シリアル化できる型に変換 Dim obj As List(Of KeyAndValue(Of TKey, TValue)) = _ ConvertDictionaryToList(dic) 'XMLファイルに保存 Dim serializer As New XmlSerializer( _ GetType(List(Of KeyAndValue(Of TKey, TValue)))) Dim sw As New StreamWriter(fileName, False, New UTF8Encoding(False)) serializer.Serialize(sw, obj) sw.Close() End Sub ''' <summary> ''' シリアル化されたXMLファイルをDictionaryに復元する ''' </summary> ''' <typeparam name="TKey">Dictionaryのキーの型</typeparam> ''' <typeparam name="TValue">Dictionaryの値の型</typeparam> ''' <param name="fileName">復元するXMLファイル名</param> ''' <returns>復元されたDictionary</returns> Public Shared Function XmlDeserialize(Of TKey, TValue)( _ fileName As String) As Dictionary(Of TKey, TValue) 'XMLファイルから復元 Dim serializer As New XmlSerializer( _ GetType(List(Of KeyAndValue(Of TKey, TValue)))) Dim sr As New StreamReader(fileName, New UTF8Encoding(False)) Dim obj As List(Of KeyAndValue(Of TKey, TValue)) = _ DirectCast(serializer.Deserialize(sr), _ List(Of KeyAndValue(Of TKey, TValue))) sr.Close() 'Dictionaryに戻す Dim dic As Dictionary(Of TKey, TValue) = ConvertListToDictionary(obj) Return dic End Function 'エントリポイント Public Shared Sub Main(args As String()) '保存先のファイル名 Dim fileName As String = "C:\test\sample.xml" 'シリアル化するDictionaryを作成 Dim dic As New Dictionary(Of Integer, String)() dic.Add(1, "いち") dic.Add(2, "に") dic.Add(3, "さん") 'DictionaryをXMLファイルに保存する XmlSerialize(fileName, dic) 'DictionaryをXMLファイルから復元する Dim dic2 As Dictionary(Of Integer, String) = _ XmlDeserialize(Of Integer, String)(fileName) End Sub End Class
using System; using System.Xml.Serialization; using System.Collections.Generic; using System.IO; using System.Text; public class Program { /// <summary> /// シリアル化できる、KeyValuePairに代わる構造体 /// </summary> /// <typeparam name="TKey">Keyの型</typeparam> /// <typeparam name="TValue">Valueの型</typeparam> [Serializable] public struct KeyAndValue<TKey, TValue> { public TKey Key; public TValue Value; public KeyAndValue(KeyValuePair<TKey, TValue> pair) { Key = pair.Key; Value = pair.Value; } } /// <summary> /// DictionaryをKeyAndValueのListに変換する /// </summary> /// <typeparam name="TKey">Dictionaryのキーの型</typeparam> /// <typeparam name="TValue">Dictionaryの値の型</typeparam> /// <param name="dic">変換するDictionary</param> /// <returns>変換されたKeyAndValueのList</returns> public static List<KeyAndValue<TKey, TValue>> ConvertDictionaryToList<TKey, TValue>(Dictionary<TKey, TValue> dic) { List<KeyAndValue<TKey, TValue>> lst = new List<KeyAndValue<TKey, TValue>>(); foreach (KeyValuePair<TKey, TValue> pair in dic) { lst.Add(new KeyAndValue<TKey, TValue>(pair)); } return lst; } /// <summary> /// KeyAndValueのListをDictionaryに変換する /// </summary> /// <typeparam name="TKey">KeyAndValueのKeyの型</typeparam> /// <typeparam name="TValue">KeyAndValueのValueの型</typeparam> /// <param name="lst">変換するKeyAndValueのList</param> /// <returns>変換されたDictionary</returns> public static Dictionary<TKey, TValue> ConvertListToDictionary<TKey, TValue>(List<KeyAndValue<TKey, TValue>> lst) { Dictionary<TKey, TValue> dic = new Dictionary<TKey, TValue>(); foreach (KeyAndValue<TKey, TValue> pair in lst) { dic.Add(pair.Key, pair.Value); } return dic; } /// <summary> /// DictionaryをXMLファイルに保存する /// </summary> /// <typeparam name="TKey">Dictionaryのキーの型</typeparam> /// <typeparam name="TValue">Dictionaryの値の型</typeparam> /// <param name="fileName">保存先のXMLファイル名</param> /// <param name="dic">保存するDictionary</param> public static void XmlSerialize<TKey, TValue>( string fileName, Dictionary<TKey, TValue> dic) { //シリアル化できる型に変換 List<KeyAndValue<TKey, TValue>> obj = ConvertDictionaryToList(dic); //XMLファイルに保存 XmlSerializer serializer = new XmlSerializer(typeof(List<KeyAndValue<TKey, TValue>>)); StreamWriter sw = new StreamWriter(fileName, false, new UTF8Encoding(false)); serializer.Serialize(sw, obj); sw.Close(); } /// <summary> /// シリアル化されたXMLファイルをDictionaryに復元する /// </summary> /// <typeparam name="TKey">Dictionaryのキーの型</typeparam> /// <typeparam name="TValue">Dictionaryの値の型</typeparam> /// <param name="fileName">復元するXMLファイル名</param> /// <returns>復元されたDictionary</returns> public static Dictionary<TKey, TValue> XmlDeserialize<TKey, TValue>( string fileName) { //XMLファイルから復元 XmlSerializer serializer = new XmlSerializer(typeof(List<KeyAndValue<TKey, TValue>>)); StreamReader sr = new StreamReader(fileName, new UTF8Encoding(false)); List<KeyAndValue<TKey, TValue>> obj = (List<KeyAndValue<TKey, TValue>>)serializer.Deserialize(sr); sr.Close(); //Dictionaryに戻す Dictionary<TKey, TValue> dic = ConvertListToDictionary(obj); return dic; } //エントリポイント public static void Main(string[] args) { //保存先のファイル名 string fileName = @"C:\test\sample.xml"; //シリアル化するDictionaryを作成 Dictionary<int, string> dic = new Dictionary<int, string>(); dic.Add(1, "いち"); dic.Add(2, "に"); dic.Add(3, "さん"); //DictionaryをXMLファイルに保存する XmlSerialize(fileName, dic); //DictionaryをXMLファイルから復元する Dictionary<int, string> dic2 = XmlDeserialize<int, string>(fileName); } }
保存されるXMLファイルの内容は、以下のようになります。
<?xml version="1.0" encoding="utf-8"?> <ArrayOfKeyAndValueOfInt32String xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <KeyAndValueOfInt32String> <Key>1</Key> <Value>いち</Value> </KeyAndValueOfInt32String> <KeyAndValueOfInt32String> <Key>2</Key> <Value>に</Value> </KeyAndValueOfInt32String> <KeyAndValueOfInt32String> <Key>3</Key> <Value>さん</Value> </KeyAndValueOfInt32String> </ArrayOfKeyAndValueOfInt32String>
シリアル化できるように、HashtableやDictionaryにIXmlSerializableインターフェイスを実装する方法もあります。この場合は、自分でどのようにシリアル化するかを決めなければなりませんので、少し面倒です。
IXmlSerializableには3つのメソッドがあります。WriteXmlメソッドでは、コレクションの内容をXMLに書き込みます。ReadXmlメソッドでは、XMLを読み込んでコレクションを作成します。GetSchemaメソッドではNothing(C#では、null)を返してスキーマ情報を省略します。
この方法による例が、以下のページにあります。
これらの例を参考にさせていただいて、私もコードを書いてみました。まずはXMLシリアル化できるDictionaryからです。このクラスは普通のDictionaryのように使用でき、「オブジェクトの内容をファイルに保存、復元する」の方法でXMLシリアル化できます。
Imports System.Collections.Generic Imports System.Xml Imports System.Xml.Serialization ''' <summary> ''' XMLシリアル化ができるDictionaryクラス ''' </summary> ''' <typeparam name="TKey">キーの型</typeparam> ''' <typeparam name="TValue">値の型</typeparam> Public Class SerializableDictionary(Of TKey, TValue) Inherits Dictionary(Of TKey, TValue) Implements IXmlSerializable 'Nothingを返す Public Function GetSchema() As System.Xml.Schema.XmlSchema _ Implements IXmlSerializable.GetSchema Return Nothing End Function 'XMLを読み込んで復元する Public Sub ReadXml(ByVal reader As XmlReader) _ Implements IXmlSerializable.ReadXml Dim wasEmpty As Boolean = reader.IsEmptyElement reader.Read() If wasEmpty Then Return End If 'XmlSerializerを用意する Dim keySerializer As New XmlSerializer(GetType(TKey)) Dim valueSerializer As New XmlSerializer(GetType(TValue)) While reader.NodeType <> XmlNodeType.EndElement reader.ReadStartElement("KeyValuePair") 'キーを逆シリアル化する reader.ReadStartElement("Key") Dim key As TKey = _ DirectCast(keySerializer.Deserialize(reader), TKey) reader.ReadEndElement() '値を逆シリアル化する reader.ReadStartElement("Value") Dim val As TValue = _ DirectCast(valueSerializer.Deserialize(reader), TValue) reader.ReadEndElement() reader.ReadEndElement() 'コレクションに追加する Me.Add(key, val) '次へ reader.MoveToContent() End While reader.ReadEndElement() End Sub '現在の内容をXMLに書き込む Public Sub WriteXml(ByVal writer As XmlWriter) _ Implements IXmlSerializable.WriteXml 'XmlSerializerを用意する Dim keySerializer As New XmlSerializer(GetType(TKey)) Dim valueSerializer As New XmlSerializer(GetType(TValue)) For Each key As TKey In Me.Keys writer.WriteStartElement("KeyValuePair") 'キーを書き込む writer.WriteStartElement("Key") keySerializer.Serialize(writer, key) writer.WriteEndElement() '値を書き込む writer.WriteStartElement("Value") valueSerializer.Serialize(writer, Me(key)) writer.WriteEndElement() writer.WriteEndElement() Next End Sub End Class
using System; using System.Collections.Generic; using System.Xml; using System.Xml.Serialization; /// <summary> /// XMLシリアル化ができるDictionaryクラス /// </summary> /// <typeparam name="TKey">キーの型</typeparam> /// <typeparam name="TValue">値の型</typeparam> public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable { //nullを返す public System.Xml.Schema.XmlSchema GetSchema() { return null; } //XMLを読み込んで復元する public void ReadXml(XmlReader reader) { bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; //XmlSerializerを用意する XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); while (reader.NodeType != XmlNodeType.EndElement) { reader.ReadStartElement("KeyValuePair"); //キーを逆シリアル化する reader.ReadStartElement("Key"); TKey key = (TKey)keySerializer.Deserialize(reader); reader.ReadEndElement(); //値を逆シリアル化する reader.ReadStartElement("Value"); TValue val = (TValue)valueSerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadEndElement(); //コレクションに追加する this.Add(key, val); //次へ reader.MoveToContent(); } reader.ReadEndElement(); } //現在の内容をXMLに書き込む public void WriteXml(XmlWriter writer) { //XmlSerializerを用意する XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); foreach (TKey key in this.Keys) { writer.WriteStartElement("KeyValuePair"); //キーを書き込む writer.WriteStartElement("Key"); keySerializer.Serialize(writer, key); writer.WriteEndElement(); //値を書き込む writer.WriteStartElement("Value"); valueSerializer.Serialize(writer, this[key]); writer.WriteEndElement(); writer.WriteEndElement(); } } }
このクラスのインスタンスをXMLシリアル化した結果は、例えば以下のようになります。
<?xml version="1.0"?> <SerializableDictionaryOfInt32String> <KeyValuePair> <Key> <int>1</int> </Key> <Value> <string>いち</string> </Value> </KeyValuePair> <KeyValuePair> <Key> <int>2</int> </Key> <Value> <string>に</string> </Value> </KeyValuePair> <KeyValuePair> <Key> <int>3</int> </Key> <Value> <string>さん</string> </Value> </KeyValuePair> </SerializableDictionaryOfInt32String>
Hashtableの場合は、キーと値の型が分かりませんので、保存する時に型の情報も保存します。それ以外は、Dictionaryの場合と同じです。
Imports System.Collections Imports System.Xml Imports System.Xml.Serialization ''' <summary> ''' XMLシリアル化ができるHashtableクラス ''' </summary> Public Class SerializableHashtable Inherits Hashtable Implements IXmlSerializable Public Function GetSchema() As System.Xml.Schema.XmlSchema _ Implements IXmlSerializable.GetSchema Return Nothing End Function Public Sub ReadXml(ByVal reader As XmlReader) _ Implements IXmlSerializable.ReadXml Dim wasEmpty As Boolean = reader.IsEmptyElement reader.Read() If wasEmpty Then Return End If Dim keySerializer As XmlSerializer Dim valueSerializer As XmlSerializer While reader.NodeType <> XmlNodeType.EndElement reader.ReadStartElement("KeyValuePair") 'キーを復元する 'キーの型を取得する Dim keyType As Type = Type.GetType(reader.GetAttribute("Type")) reader.ReadStartElement("Key") 'キーの値を取得する keySerializer = New XmlSerializer(keyType) Dim key As Object = keySerializer.Deserialize(reader) reader.ReadEndElement() '値を復元する '値の型を取得する Dim valueType As Type = Type.GetType(reader.GetAttribute("Type")) reader.ReadStartElement("Value") '値の値を取得する valueSerializer = New XmlSerializer(valueType) Dim val As Object = valueSerializer.Deserialize(reader) reader.ReadEndElement() reader.ReadEndElement() 'コレクションに追加する Me.Add(key, val) '次へ reader.MoveToContent() End While reader.ReadEndElement() End Sub Public Sub WriteXml(ByVal writer As XmlWriter) _ Implements IXmlSerializable.WriteXml Dim keySerializer As XmlSerializer Dim valueSerializer As XmlSerializer For Each key As Object In Me.Keys writer.WriteStartElement("KeyValuePair") 'キーを書き込む writer.WriteStartElement("Key") 'キーの型を書き込む Dim keyType As Type = key.GetType() writer.WriteAttributeString("Type", keyType.FullName) 'キーをシリアル化して書き込む keySerializer = New XmlSerializer(keyType) keySerializer.Serialize(writer, key) writer.WriteEndElement() '値を書き込む writer.WriteStartElement("Value") '値の型を書き込む Dim val As Object = Me(key) Dim valueType As Type = val.GetType() writer.WriteAttributeString("Type", valueType.FullName) '値をシリアル化して書き込む valueSerializer = New XmlSerializer(valueType) valueSerializer.Serialize(writer, val) writer.WriteEndElement() writer.WriteEndElement() Next End Sub End Class
using System; using System.Collections; using System.Xml; using System.Xml.Serialization; /// <summary> /// XMLシリアル化ができるHashtableクラス /// </summary> public class SerializableHashtable : Hashtable, IXmlSerializable { public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; XmlSerializer keySerializer; XmlSerializer valueSerializer; while (reader.NodeType != XmlNodeType.EndElement) { reader.ReadStartElement("KeyValuePair"); //キーを復元する //キーの型を取得する Type keyType = Type.GetType(reader.GetAttribute("Type")); reader.ReadStartElement("Key"); //キーの値を取得する keySerializer = new XmlSerializer(keyType); object key = keySerializer.Deserialize(reader); reader.ReadEndElement(); //値を復元する //値の型を取得する Type valueType = Type.GetType(reader.GetAttribute("Type")); reader.ReadStartElement("Value"); //値の値を取得する valueSerializer = new XmlSerializer(valueType); object val = valueSerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadEndElement(); //コレクションに追加する this.Add(key, val); //次へ reader.MoveToContent(); } reader.ReadEndElement(); } public void WriteXml(XmlWriter writer) { XmlSerializer keySerializer; XmlSerializer valueSerializer; foreach (object key in this.Keys) { writer.WriteStartElement("KeyValuePair"); //キーを書き込む writer.WriteStartElement("Key"); //キーの型を書き込む Type keyType = key.GetType(); writer.WriteAttributeString("Type", keyType.FullName); //キーをシリアル化して書き込む keySerializer = new XmlSerializer(keyType); keySerializer.Serialize(writer, key); writer.WriteEndElement(); //値を書き込む writer.WriteStartElement("Value"); //値の型を書き込む object val = this[key]; Type valueType = val.GetType(); writer.WriteAttributeString("Type", valueType.FullName); //値をシリアル化して書き込む valueSerializer = new XmlSerializer(valueType); valueSerializer.Serialize(writer, val); writer.WriteEndElement(); writer.WriteEndElement(); } } }
このクラスのインスタンスをXMLシリアル化すると、例えば以下のようになります。
<?xml version="1.0"?> <SerializableHashtable> <KeyValuePair> <Key Type="System.Int32"> <int>3</int> </Key> <Value Type="System.String"> <string>さん</string> </Value> </KeyValuePair> <KeyValuePair> <Key Type="System.Int32"> <int>2</int> </Key> <Value Type="System.String"> <string>に</string> </Value> </KeyValuePair> <KeyValuePair> <Key Type="System.Int32"> <int>1</int> </Key> <Value Type="System.String"> <string>いち</string> </Value> </KeyValuePair> </SerializableHashtable>