2010年1月28日木曜日

.NET 定数ファイルを作成する ~その8~ 定数のシリアライズ・デシリアライズをカスタマイズする

.NET 定数ファイルを作成する ~その1~
.NET 定数ファイルを作成する ~その2~
.NET 定数ファイルを作成する ~その3~
.NET 定数ファイルを作成する ~その4~ インテリセンスに候補を表示する
.NET 定数ファイルを作成する ~その5~ 定数のリストを作成する
.NET 定数ファイルを作成する ~その6~ 定数を「=」演算子で比較できるようにする
.NET 定数ファイルを作成する ~その7~ 定数をシリアライズ・デシリアライズできるようにする

.NET シリアライズ・デシリアライズ ~XmlSerializerクラス~

前回は定数アイテムをシリアライズ出来るようにしました。
出力されたXMLファイルをみるとWeekDayクラスのCd,Name,AliasNameがすべて出力されています。

前回出力したXMLファイル
<?xml version="1.0"?>
<SerializeSample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Cd>1</Cd>
  <WeekDay>
    <Cd>6</Cd>
    <Name>金曜日</Name>
    <AliasName>金</AliasName>
  </WeekDay>
</SerializeSample>

このファイルからデシリアライズするとSerializeSampleオブジェクトのWeekDayTypeオブジェクトはCd=6,Name=金曜日,AliasName=金となります。

WeekDayTypeクラスのAliasNameを「FriDay」に変更したとします。
Public Shared ReadOnly Fri As New WeekDayType(6, "金曜日", "FriDay")

その後、前回出力したXMLファイルからSerializeSampleオブジェクトをデシリアライズすると、
SerializeSampleオブジェクトのWeekDayTypeオブジェクトはCd=6,Name=金曜日,AliasName=金のままです。

シリアライズはWeekDayTypeオブジェクトのCDだけを出力し、
デシリアライズはそのCDを元に、WeekDayTypeクラスのPublic Shared ReadOnlyフィールドからオブジェクトを復元したいと思います。

出力したいXMLファイル
<?xml version="1.0"?>
<SerializeSample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Cd>1</Cd>
  <WeekDay>6</WeekDay>
</SerializeSample>

ConstantItemのNameプロパティとAliasNameプロパティにXmlIgnore属性をつければ、
シリアライズ対象外になりXMLファイルには出力されないようになります。
このようにして出力したXMLファイルからSerializeSampleオブジェクトをデシリアライズすると、
SerializeSampleオブジェクトのWeekDayTypeオブジェクトはCd=6,Name="",AliasName=""となります。
XmlIgnore属性では対応できない細かな制御はIXmlSerializableインターフェイスを実装し、
WriteXmlメソッド、ReadXmlメソッドに記述します。

またデシリアライズで復元したオブジェクトはWeekDayTypeクラスのPublic Shared ReadOnlyで定義されている定数オブジェクトとは異なるため、
Equalsメソッドで比較するとFalseを返すようになります。
そこでEqualsメソッドではCdが同じならTrueを返すようオーバーライドします。
EqualsメソッドをオーバーライドしたのでGetHashCodeメソッドもオーバーライドします。
GetHashCodeメソッドはEqualsメソッドがTrueならば、そのオブジェクトは同じHashCodeを返すようにしなければいけません。
今回はCdの比較だけなので、GetHashCodeメソッドはCdを返せばよいです。
2つ以上の値をEqualsメソッドで比較した場合などはそれぞれの値をXorします。


Imports System.Xml
Imports System.Xml.Serialization

Namespace Constant
    ''' <summary>
    ''' 定数アイテムの基底クラスです。
    ''' </summary>
     _
    Public MustInherit Class ConstantItem
        Implements IXmlSerializable

        '-----フィールド-----

        ''' <summary>コード</summary>
        Protected _iCd As Integer
        ''' <summary>名前</summary>
        Protected _sName As String
        ''' <summary>別名</summary>
        Protected _sAliasName As String

        '-----Publicプロパティ-----

        ''' <summary>コードを取得します</summary>
        Public Property Cd() As Integer
            Get
                Return Me._iCd
            End Get
            Set(ByVal value As Integer)
                Me._iCd = value
            End Set
        End Property
        ''' <summary>名前を取得します。</summary>
        Public Property Name() As String
            Get
                Return Me._sName
            End Get
            Set(ByVal value As String)
                Me._sName = value
            End Set
        End Property
        ''' <summary>別名を取得します。</summary>
        Public Property AliasName() As String
            Get
                Return Me._sAliasName
            End Get
            Set(ByVal value As String)
                Me._sAliasName = value
            End Set
        End Property

        '-----コンストラクタ-----

        ''' <summary>引数を取らないコンストラクタ</summary>
        Protected Sub New()
            'シリアライズの為に必要です。
        End Sub
        ''' <summary>コードと名前を引数に取るコンストラクタ</summary>
        Public Sub New(ByVal cd As Integer, ByVal name As String)
            MyBase.New()
            Me._iCd = cd
            Me._sName = name
        End Sub
        ''' <summary>コードと名前と別名を引数に取るコンストラクタ</summary>
        Public Sub New(ByVal cd As Integer, ByVal name As String, ByVal aliasname As String)
            Me.New(cd, name)
            Me._sAliasName = aliasname
        End Sub

        '-----Public Overridesメソッド-----

        ''' <summary>
        ''' ToStringメソッド
        ''' </summary>
        Public Overrides Function ToString() As String
            Return String.Format("Cd={0},Nmae={1},AliasName={2}", Me._iCd, Me._sName, Me._sAliasName)
        End Function
        
        ''' <summary>
        ''' Equalsメソッド
        ''' </summary>
        Public Overrides Function Equals(ByVal obj As Object) As Boolean
            If (obj Is Nothing) OrElse (Not Me.GetType() Is obj.GetType()) Then
                Return False
            Else
                Return (Me.Cd = DirectCast(obj, ConstantItem).Cd)
            End If
        End Function

        ''' <summary>
        ''' GetHashCodeメソッド
        ''' </summary>
        Public Overrides Function GetHashCode() As Integer
            Return Me.Cd
        End Function
        
        '-----Operator-----

        ''' <summary>
        ''' =オペレータ
        ''' </summary>
        Public Shared Operator =(ByVal left As ConstantItem, ByVal right As ConstantItem) As Boolean
            'cdが同じなら=の結果もtrueとする。
            If (left Is Nothing) OrElse (right Is Nothing) Then
                Return (left Is Nothing) And (right Is Nothing)
            Else
                Return (left.Cd = right.Cd)
            End If
        End Operator

        ''' <summary>
        ''' <>オペレータ
        ''' </summary>
        Public Shared Operator <>(ByVal left As ConstantItem, ByVal right As ConstantItem) As Boolean
            If (left Is Nothing) OrElse (right Is Nothing) Then
                Return Not ((left Is Nothing) And (right Is Nothing))
            Else
                Return Not (left.Cd = right.Cd)
            End If
        End Operator

        '-----Protectedメソッド-----

        ''' <summary>
        ''' 定数定義リストを取得します。デシリアライズの為に必要です。
        ''' </summary>
        Protected MustOverride Function GetItems() As IList

        '-----IXmlSerializableインタフェースのimplementsメソッド-----

        ''' <summary>
        ''' IXmlSerializableインターフェイスを実装する場合、このメソッドから Nothingを返す必要があります。
        ''' </summary>
        Public Function GetSchema() As System.Xml.Schema.XmlSchema _
        Implements System.Xml.Serialization.IXmlSerializable.GetSchema
            Return Nothing
        End Function

        ''' <summary>
        ''' オブジェクトの XML 表現からオブジェクトを生成します。
        ''' </summary>
        Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) _
        Implements System.Xml.Serialization.IXmlSerializable.ReadXml
            'デシリアライズするときは、CdからName,AliasNameを検索します。
            '定数定義リストを返すメソッド  Protected MustOverride Function GetItems() As IListを
            '派生クラスでオーバーライドする必要があります。
          
            reader.Read()
            
            If reader.HasValue Then
                Dim icd As Integer = Integer.Parse(reader.ReadString)
                For Each o As Object In GetItems()
                    Dim item As ConstantItem = DirectCast(o, ConstantItem)
                    If item.Cd = icd Then
                        Me._iCd = item.Cd
                        Me._sName = item.Name
                        Me._sAliasName = item.AliasName
                        Exit For
                    End If
                Next
            End If

            reader.ReadEndElement()
        End Sub

        ''' <summary>
        ''' オブジェクトを XML 表現に変換します。
        ''' </summary>
        Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) _
        Implements System.Xml.Serialization.IXmlSerializable.WriteXml
            'シリアライズするときは、Cdのみをシリアライズします。
            writer.WriteValue(Me._iCd)
        End Sub

    End Class
End Namespace


WeekDayTypeクラスではGetItemsメソッドをオーバーライドします。
Namespace Constant

    ''' <summary>
    ''' 曜日
    ''' </summary>
    ''' 
    Public NotInheritable Class WeekDayType : Inherits ConstantItem

        Public Shared Shadows ReadOnly Items As New ConstantItemCollection(Of WeekDayType)

        Public Shared ReadOnly Sun As New WeekDayType(1, "日曜日", "日")
        Public Shared ReadOnly Mon As New WeekDayType(2, "月曜日", "月")
        Public Shared ReadOnly Tue As New WeekDayType(3, "火曜日", "火")
        Public Shared ReadOnly Wed As New WeekDayType(4, "水曜日", "水")
        Public Shared ReadOnly Thr As New WeekDayType(5, "木曜日", "木")
        Public Shared ReadOnly Fri As New WeekDayType(6, "金曜日", "金")
        Public Shared ReadOnly Sat As New WeekDayType(7, "土曜日", "土")

        Private Sub New(ByVal cd As Integer, ByVal name As String, ByVal aliasname As String)
            MyBase.new(cd, name, aliasname)
            Items.Add(Me)

        End Sub
        '---以下シリアライズの為に必要です。-----
        Private Sub New()
        End Sub

        Protected Overrides Function GetItems() As System.Collections.IList
            Return Items
        End Function

    End Class

   
End Namespace

ConstantItemCollectionクラス 前回より変更はありません。
Namespace Constant
    ''' <summary>
    ''' 定数アイテムを管理するコレクションクラスです。
    ''' </summary>
    Public Class ConstantItemCollection(Of T As ConstantItem)
        Inherits List(Of T)
        Implements ICloneable


        ''' <summary>
        ''' 定義されている定数アイテムより、引数に指定したcdをもつアイテムを検索します。
        ''' </summary>
        Public Function GetItem(ByVal cd As Integer) As T
            For idx As Integer = 0 To Me.Count - 1
                If cd = CType(Me.Item(idx), ConstantItem).Cd Then
                    Return CType(Me.Item(idx), T)
                End If
            Next
            Return Nothing
        End Function

        ''' <summary>
        ''' 定義されている定数アイテムより、引数に指定したnameをもつアイテムを検索します。
        ''' </summary>
        Public Function GetItem(ByVal name As String) As T
            For idx As Integer = 0 To Me.Count - 1
                If name = CType(Me.Item(idx), ConstantItem).Name Then
                    Return CType(Me.Item(idx), T)
                End If
            Next
            Return Nothing
        End Function

        ''' <summary>
        ''' Copyメソッド
        ''' </summary>
        ''' 
        ''' 
        Public Function Copy() As ConstantItemCollection(Of T)
            Return DirectCast(Me.Clone(), ConstantItemCollection(Of T))
        End Function

        '-----ICloneableインターフェースのimplementsメソッド-----

        ''' <summary>
        ''' Cloneメソッド
        ''' </summary>
        Private Function Clone() As Object Implements System.ICloneable.Clone
            '戻り値がObject型だと不便なのでPrivateにしCopyメソッドをPublicで公開します。
            Return Me.MemberwiseClone()
        End Function
    End Class
End Namespace

これでシリアライズではWeekDayTypeオブジェクトのCDだけを出力し、
デシリアライズはそのCDを元に、WeekDayTypeクラスのPublic Shared ReadOnlyフィールドからオブジェクトを復元できました。

テストのためにWeekDayTypeクラスをフィールドメンバにもつクラスを作成します。
<Serializable()> _
Public Class SerializeSample

    Private _cd As Integer
    Private _weekDay As Constant.WeekDayType

    Public Property Cd() As Integer
        Get
            Return Me._cd
        End Get
        Set(ByVal value As Integer)
            Me._cd = value
        End Set
    End Property

    Public Property WeekDay() As Constant.WeekDayType
        Get
            Return Me._weekDay
        End Get
        Set(ByVal value As Constant.WeekDayType)
            Me._weekDay = value
        End Set
    End Property

    Public Sub New()
    End Sub

End Class
WeekDayTypeクラスをフィールドメンバにもつSerializeSampleクラスをシリアライズ・デシリアライズします。
Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'シリアライズ
        Dim ss As New SerializeSample
        ss.Cd = 1
        ss.WeekDay = Constant.WeekDayType.Fri

        Using fs As New System.IO.FileStream("D:\test.xml", IO.FileMode.Create, IO.FileAccess.Write)
            Dim xs As New System.Xml.Serialization.XmlSerializer(GetType(SerializeSample))
            xs.Serialize(fs, ss)
            fs.Close()
        End Using
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        'デシリアライズ
        Dim ss As SerializeSample

        Using fs As New System.IO.FileStream("D:\test.xml", IO.FileMode.Open)
            Dim xs As New System.Xml.Serialization.XmlSerializer(GetType(SerializeSample))
            ss = CType(xs.Deserialize(fs), SerializeSample)
            fs.Close()
        End Using

        Console.WriteLine(ss.Cd)
        Console.WriteLine(ss.WeekDay.ToString)
    End Sub
End Class

0 件のコメント: