XMLファイルをコピーする(3)

こんどは.NETのSystem.Xml.XmlReaderを使ってVisual Basicでやってみます.プログラムは以下のようなものです.(あいかわらずエラー処理は全然やってありません.)

Imports System.IO
Imports System.Xml

Module Main

    Sub Main()
        Dim xr = CreateXmlReader("MusicLibrary.xml")
        Dim sw As StreamWriter = CreateStreamWiter("MusicLibrary_Copy.xml")
        XmlCopy(xr, sw)
        sw.Close()
    End Sub

    Function CreateStreamWiter(ByVal outputPath As String) As StreamWriter
        Dim sw As StreamWriter = New StreamWriter(outputPath, False, System.Text.Encoding.GetEncoding("UTF-8"))
        sw.Write("<?xml version=""1.0"" encoding=""UTF-8""?>")
        Return sw
    End Function

    Function CreateXmlReader(ByVal inputPath As String) As XmlReader
        Dim rs As XmlReaderSettings = New XmlReaderSettings
        rs.IgnoreProcessingInstructions = True
        rs.IgnoreComments = True
        rs.ConformanceLevel = ConformanceLevel.Document
        Dim xr As XmlReader = XmlReader.Create(inputPath, rs)
        Return xr
    End Function

    Sub XmlCopy(xr As XmlReader, sw As StreamWriter)
        While xr.Read
            Select Case xr.NodeType
                Case XmlNodeType.Element
                    Dim elementName As String = xr.Name
                    Dim attributes As List(Of Attribute) = New List(Of Attribute)
                    While xr.MoveToNextAttribute
                        Dim attr As New Attribute
                        attr.setAttr(xr.Name, xr.ReadContentAsString)
                        attributes.Add(attr)
                    End While
                    StartElement(sw, elementName, attributes)
                Case XmlNodeType.EndElement
                    Dim elementName As String = xr.Name
                    EndElement(sw, elementName)
                Case XmlNodeType.Text
                    Dim text As String = xr.Value
                    OutText(sw, text)
                Case XmlNodeType.Whitespace
                    Dim text As String = xr.Value
                    OutText(sw, text)
            End Select
        End While
    End Sub

    Structure Attribute
        Dim name As String
        Dim value As String
        Sub setAttr(name As String, value As String)
            Me.name = name
            Me.value = value
        End Sub
    End Structure

    Sub StartElement(sw As StreamWriter, elementName As String, ByVal attributes As List(Of Attribute))
        sw.Write("<" + elementName)
        For Each attribute As Attribute In attributes
            sw.Write(" " + attribute.name + "=""" + attribute.value + """")
        Next
        sw.Write(">")
    End Sub

    Sub EndElement(sw As StreamWriter, elementName As String)
        sw.Write("</" + elementName + ">")
    End Sub

    Sub OutText(sw As StreamWriter, text As String)
        sw.Write(text)
    End Sub

End Module

System.Xml.XmlReaderはPULL型のパーサーです.プログラム側からReadメソッドを呼び出して、コンテンツを取得します.これをXMLファイルの終了まで繰り返します.

コンテンツを取り出したら、そのノードタイプを判定して、Javaの場合と同じような処理を行います.

1.要素の始まりでは"<"+要素名+属性値+">"をPrintStreamに書き出します.
2.要素間のテキストはそのまま内容をPrintStreamに書き出します.
3.要素の終了では"</"+要素名+">"をPrintStreamに書き出します.

属性を記憶するために、

    Structure Attribute
        Dim name As String
        Dim value As String
        Sub setAttr(name As String, value As String)
            Me.name = name
            Me.value = value
        End Sub
    End Structure

という構造体を定義して、

Dim attributes As List(Of Attribute) = New List(Of Attribute)

というリストに記憶しています.

プログラム構造からわかりますが、

1.PULL型なのでプログラムの側でコンテンツの読み出しを制御することができます.
2.しかしあくまでもイベント処理です.SAXと同じでReadメソッドで取得したコンテンツはその時点で処理できなければ、自分で記憶する必要があります.

次は同じVBLINQを使った例を見てみたいと思います.そのあとはF#とScalaです.

JavaでもPULL型のパーサーにStAXがあります.