DOBON.NET

HashtableやDictionaryオブジェクトをXMLシリアル化する

オブジェクトの内容をファイルに保存、復元する」で説明している方法でHashtableやDictionaryオブジェクトをXMLファイルに保存(シリアル化)しようとすると、例外NotSupportedException(型 System.Collections.Hashtable は IDictionary を実装するため、サポートされません)が発生します。ここではこの回避法を幾つか紹介します。

なおここでは「オブジェクトの内容をファイルに保存、復元する」で説明している事柄については一切説明しませんので、基本的な部分が分からないという場合は、まずそちらをご覧ください。

DataContractSerializerを使用する

.NET Framework 3.0で追加されたDataContractSerializerクラスを使うと、HashtableやDictionaryをシリアル化できます。DataContractSerializerの使い方は、「DataContractSerializerを使って、オブジェクトのXMLシリアル化、逆シリアル化を行う」で説明しています。

シリアル化できる配列やコレクションに変換する

あまりスマートなやり方ではありませんが、HashtableやDictionaryをシリアル化できる型に変換する方法が考えられます。

Hashtableをシリアル化する

以下の例では、HashtableをDictionaryEntry構造体の配列に変換してシリアル化し、逆シリアル化する時は、逆の変換で元に戻しています。XmlSerializeメソッドでHashtableをXMLファイルに保存し、XmlDeserializeメソッドで復元できます。

VB.NET
コードを隠すコードを選択
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
C#
コードを隠すコードを選択
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をシリアル化する

Dictionaryの場合はKeyValuePair構造体のコレクションに変換してシリアル化したいところですが、KeyValuePairのプロパティは読み取り専用ですので、シリアル化できません。そこで、KeyValuePairに代わるシリアル化できる型を自分で用意します。

下の例では、Hashtebleの例と同様に、XmlSerializeメソッドでXMLファイルに保存し、XmlDeserializeメソッドで復元しています。

VB.NET
コードを隠すコードを選択
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
C#
コードを隠すコードを選択
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を実装する

シリアル化できるように、HashtableやDictionaryにIXmlSerializableインターフェイスを実装する方法もあります。この場合は、自分でどのようにシリアル化するかを決めなければなりませんので、少し面倒です。

IXmlSerializableには3つのメソッドがあります。WriteXmlメソッドでは、コレクションの内容をXMLに書き込みます。ReadXmlメソッドでは、XMLを読み込んでコレクションを作成します。GetSchemaメソッドではNothing(C#では、null)を返してスキーマ情報を省略します。

この方法による例が、以下のページにあります。

シリアル化できるDictionaryを作成する

これらの例を参考にさせていただいて、私もコードを書いてみました。まずはXMLシリアル化できるDictionaryからです。このクラスは普通のDictionaryのように使用でき、「オブジェクトの内容をファイルに保存、復元する」の方法でXMLシリアル化できます。

VB.NET
コードを隠すコードを選択
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
C#
コードを隠すコードを選択
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を作成する

Hashtableの場合は、キーと値の型が分かりませんので、保存する時に型の情報も保存します。それ以外は、Dictionaryの場合と同じです。

VB.NET
コードを隠すコードを選択
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
C#
コードを隠すコードを選択
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>
  • 履歴:
  • 2014/5/7 FileStreamの代わりにStreamWriterを使うようにした。

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

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

この記事への評価

この記事へのコメント

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