シリアル化処理をするクラスに配列のプロパティがあるのはよくあることですが、 何も考えずにコーディングすると以下の制約にひっかかることが多いと思います。
サンプルのコード
Public Property Hoges As New List(Of Hoge)
CA1002: ジェネリック リストを公開しません
Collection(Of T)
などで公開して解決できます。なおIList(Of T)
ではシリアル化ができないのでNG。
なお、Collection(Of T)
の名前空間はSystem.Collections.ObjectModel
ですので、適宜インポートしてください。
Public Property Hoges As New Collection(Of Hoge)
CA2227: Collection プロパティは読み取り専用でなければなりません
CA2227: Collection プロパティは読み取り専用でなければなりません
ReadOnlyプロパティにすることで解決します。
Pricvate _hoges As New Collection(Of Hoge)
Public Property Hoges As Collection(Of Hoge)
Get
Return _hoges
End Get
End Property
が、「CA2227」の対応したことでシリアル化の対象からはずれてしまいました…
読み取り専用コレクションをシリアル化する
こちらを見ると
XML シリアル化では、メソッド、インデクサー、プライベート フィールド、または読み取り専用プロパティ (読み取り専用コレクションを除く) は変換されません。
とあります。読み取り専用プロパティはXMLシリアル化されないとありますが、但し書きで「読み取り専用コレクションは除く」とされています。ん?さっき読み取り専用コレクションにしたのにシリアル化されなかったんですが…
もう少し読んでいきましょう。
シリアル化できる項目
XmLSerializer クラスを使用して、次の項目をシリアル化できます。
- パブリックな読み取り/書き込みプロパティとパブリック クラスのフィールド
- ICollection または IEnumerable を実装するクラス
注 パブリック プロパティではなく、コレクションのみがシリアル化されます。
いまだよくわかりません。Collection(Of T)ってICollection を実装していないの?そんな訳ないよね。*1
もう少し読んでいきましょう。
XML シリアル化に関する考慮事項
IEnumerable または ICollection を異なる方法で実装しているクラスは、次のような特定の要件を満たしていれば、XmlSerializer で処理できます。
IEnumerable を実装するクラスは、単一のパラメーターを受け取るパブリックな Add メソッドを実装している必要があります。 Add メソッドのパラメーターは、GetEnumerator メソッドによって返される IEnumerator.Current プロパティから返される型と一致している (ポリモーフィックである) 必要があります。
IEnumerable の他に ICollection も実装するクラス (CollectionBase など) は、整数を受け取るパブリックなインデックス付き Item プロパティ (C# の場合はインデクサー) と、integer 型のパブリックな Count プロパティを持つ必要があります。 Add メソッドに渡されるパラメーターは、Item プロパティから返された型と同じ型か、またはその型の基本型の 1 つである必要があります。
ICollection を実装するクラスの場合、シリアル化される値は、GetEnumerator を呼び出して取得されるのではなく、インデックス付き Item プロパティから取得されます。 また、パブリック フィールドとパブリック プロパティは、別のコレクション クラス (ICollection を実装するクラス) を返すパブリック フィールドを除き、シリアル化されません。 例については、「XML シリアル化の例」を参照してください。
正直なところ、よくわかりません。独自でICollectionを実装すればうまくいきそうなので、そのようにします。
Pricvate _hoges As New HogeCollection
Public Property Hoges As HogeCollection
Get
Return _hoges
End Get
End Property
Public Class HogeCollection
Implements ICollection(Of Hoge)
Private Property Items As New Collection(Of Hoge)
Public Sub Add(item As Hoge) Implements ICollection(Of Hoge).Add
Me.Items.Add(item)
End Sub
Public Sub Clear() Implements ICollection(Of Hoge).Clear
Me.Items.Clear()
End Sub
Public Function Contains(item As Hoge) As Boolean Implements ICollection(Of Hoge).Contains
Return Me.Items.Contains(item)
End Function
Public Sub CopyTo(array() As Hoge, arrayIndex As Integer) Implements ICollection(Of Hoge).CopyTo
Me.CopyTo(array, arrayIndex)
End Sub
Public ReadOnly Property Count As Integer Implements ICollection(Of Hoge).Count
Get
Return Me.Items.Count
End Get
End Property
Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of Hoge).IsReadOnly
Get
Return False
End Get
End Property
Public Function Remove(item As Hoge) As Boolean Implements ICollection(Of Hoge).Remove
Return Me.Items.Remove(item)
End Function
Public Function GetEnumerator() As IEnumerator(Of Hoge) Implements IEnumerable(Of Hoge).GetEnumerator
Return Me.Items.GetEnumerator
End Function
Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
Return Me.Items.GetEnumerator
End Function
End Class
このコードでシリアル化されることは確認できました。若干消化不良ですが良しとしましょう。