連載

.NETで簡単XML

第11回 オブジェクトをXMLでシリアライズ(3)

株式会社ピーデー 川俣 晶
2003/11/22

自作コレクションのシリアライズ

 さて、ArrayListクラスは配列と同じようにシリアライズ可能であることが分かったわけだが、すべてのクラスが配列と同じようにシリアライズ可能というわけではない。では、シリアライズ可能なクラスと、そうではないクラスの間には、どのような違いがあるのだろうか。もし、その違いが明確に分かれば、ArrayListクラスと同様に、配列と同じようにシリアライズ可能なクラスを自作することも可能になる。

 この条件は、クラス・ライブラリのリファレンスを見ると、XmlSerializerクラスの解説に書かれている。そこでは、配列と同じようにシリアライズ可能にする2つの方法が説明されている。最初の方法は、IEnumerableインターフェイスを実装する方法である。IEnumerableインターフェイスがあれば、そのインスタンスが保持している情報を列挙して取得でき、シリアライズが実現できる。これに加えて、1つの引数を持つAddメソッドが必要であるとしている。これは、特にインターフェイスを実装するような形を取る必要はなく、ただAddというメソッドを記述すればよい。これによって、インスタンスに情報を追加することができるようになり、デシリアライズが実現できる。

 もう1つの方法は、IEnumerableインターフェイスに加えてICollectionインターフェイスを実装する方法である。クラス・ライブラリのリファレンスによれば、「整数を受け取るパブリックなItemインデックス付きプロパティ(C#のインデクサ)および整数型のパブリックなCountプロパティ」が必要であるとしている。この場合、クラス内の情報を得るためにIEnumerableインターフェイスを使うのではなく、Countプロパティでデータの個数を確認し、Itemプロパティ(C#ならインデクサ)を経由して、シリアライズすべきデータが取得されることになる。

 実際に、IEnumerableインターフェイスを実装して、配列と同じようにシリアライズ可能なクラスを作成した例を以下に示す。

 serializeSample、deserializeSample、Form1_Loadの各メソッドは前の例と同じなので省略している。

Public Class Person
  Private m_name As String = "未設定"
  Private m_age As Integer = 0
  Public Property Name() As String
    Get
      Return m_name
    End Get
    Set(ByVal Value As String)
      m_name = Value
    End Set
  End Property

  Public Property Age() As Integer
    Get
      Return m_age
    End Get
    Set(ByVal Value As Integer)
      m_age = Value
    End Set
  End Property
End Class

Public Class PersonsEnumerator
  Implements IEnumerator

  Private personsList As ArrayList
  Private pointer As Integer

  Public Overridable ReadOnly Property Current() As Object Implements IEnumerator.Current
    Get
      Return personsList(pointer)
    End Get
  End Property

  Public Overridable Function MoveNext() As Boolean Implements IEnumerator.MoveNext
    pointer += 1
    If pointer >= personsList.Count Then Return False
    Return True
  End Function

  Public Overridable Sub Reset() Implements IEnumerator.Reset
    pointer = -1
  End Sub

  Public Sub New(ByVal personsList As ArrayList)
    Me.personsList = CType(personsList.Clone(), ArrayList)
    Reset()
  End Sub

End Class

Public Class EnumerablePersons
  Implements IEnumerable

  Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
    Return New PersonsEnumerator(PersonsList)
  End Function

  Public ID As String = "abc"
  Private PersonsList As New ArrayList

  Public Sub Add(ByVal person As Object)
    PersonsList.Add(person)
  End Sub
End Class

Public Class Persons
  <XmlArrayItem(Type:=GetType(Person))> _
  Public PersonsList As EnumerablePersons
  Public Sub SetPersons(ByVal person1 As Person, ByVal person2 As Person, ByVal person3 As Person)
    PersonsList = New EnumerablePersons
    PersonsList.Add(person1)
    PersonsList.Add(person2)
    PersonsList.Add(person3)
  End Sub

  Public Sub Dump()
    For Each person As Person In PersonsList
      System.Diagnostics.Trace.WriteLine(person.Name)
      System.Diagnostics.Trace.WriteLine(person.Age)
    Next
  End Sub
End Class
サンプル・プログラム4:IEnumerableインターフェイスを実装した例(VB.NET版C#版

 これを実行すると以下のようになる。

山田太郎
17
山田二郎
16
山田三郎
15

 生成されるXML文書は以下のようになる。

<?xml version="1.0"?>
<Persons xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <PersonsList>
    <Person>
      <Name>山田太郎</Name>
      <Age>17</Age>
    </Person>
    <Person>
      <Name>山田二郎</Name>
      <Age>16</Age>
    </Person>
    <Person>
      <Name>山田三郎</Name>
      <Age>15</Age>
    </Person>
  </PersonsList>
</Persons>
生成されるXML文書

 さて、このサンプル・プログラムは実質的にArrayListクラスでデータの集まり(コレクション)を実現しており、これほど長いコードを記述する必要はないのだが、サンプルということで、あえてすべて明示的に記述してみた。EnumerablePersonsクラスは、列挙可能なPersonクラスのコレクションという機能を持つ。これは、IEnumerableインタフェイスを実装している。実際にIEnumerableインターフェイスのGetEnumeratorメソッドが返す値を実現するために、PersonsEnumeratorクラスが用意されている。このクラスに、Currentプロパティ、MoveNextメソッド、Resetメソッドが記述されている。これだけあればシリアライズ可能である。

 デシリアライズを可能にするには、さらにAddメソッドが必要である。このメソッドは戻り値がなく、引数はObject型を1つだけ持つ必要がある。クラス・ライブラリのリファレンス・マニュアルを見ると、引数はObject型ではなくてもよいように読めるが、実際に筆者が試してみたところ、Object型である必要があった。

 最後にもう1つ注意を書いておこう。EnumerablePersons型のフィールドを宣言するところにXmlArrayItem属性が付いていることを確認していただきたい。どのような型のデータを読み書きするか、このコードからは自動的に読み取れないので、この属性を付けることが必要だ。その点で、ArrayListクラスを使う場合と同じである。End of Article


 INDEX
  .NETで簡単XML
  第11回 オブジェクトをXMLでシリアライズ(3)
    1.派生クラスを含む配列のシリアライズ
    2.派生クラスを含む配列をシリアライズするプログラムの内容
    3.要素のシーケンスとしての配列のシリアライズ
    4.ArrayListオブジェクトのシリアライズ
  5.自作コレクションのシリアライズ
 
インデックス・ページヘ  「連載 :.NETで簡単XML」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間