生成用于写出XML文件的代码的最佳方法

时间:2016-10-19 21:30:31

标签: xml vb.net

我有一个代码生成器来构建基于XML数据的类。它使(将会)直接更新数据。例如,假设我有这个XML

<?xml version="1.0" encoding="utf-8"?>
<Records>
    <Band>Black Sabbath
        <Album>
            Paranoid
            <Date>1977</Date>
        </Album>
    </Band>
    <Band>Iron Maiden
        <Album>
            Killers
            <Date>1981</Date>
        </Album>
        <Album>
            PeiceOfMind
            <Date>1983</Date>
        </Album>
    </Band>
</Records>

它将创建类似于这些的类(我省略了大部分功能只是为了显示结构。

Public Class RecordsNode
    Inherits XmlClassBase


    Private _current As RecordsItem
    Private _errors As List(Of String)
    Private _orphans As Dictionary(Of String, List(Of XmlClassBase))


    Public Class BandNode
        Inherits XmlClassBase


        Private _current As BandItem
        Private _list As BandItem()


        Public Class AlbumNode
            Inherits XmlClassBase


            Private _current As AlbumItem
            Private _list As AlbumItem()


            Public Class DateNode
                Inherits XmlClassBase


                Private _current As DateItem
                Private _list As DateItem()



                Public Sub New()

                    _current = New DateItem(0)

                End Sub


                Public Overrides ReadOnly Property Attributes As Dictionary(Of String, String)
                    Get
                        Return _current.Attributes
                    End Get
                End Property

                Public ReadOnly Property Current As DateItem
                    Get
                        Return _current
                    End Get
                End Property

                Public ReadOnly Property List As DateItem()
                    Get
                        Return _list
                    End Get
                End Property

                Public Overrides Property Text As String
                    Get
                        Return _current.Text
                    End Get
                    Set(value As String)
                        _current.Text = value
                    End Set
                End Property


                Public Overrides Sub Add()

                    If _list IsNot Nothing Then
                        ReDim Preserve _list(_list.GetUpperBound(0) + 1)
                    Else
                        ReDim _list(0)
                    End If


                    _current = New DateItem(_list.GetUpperBound(0))
                    _list(_list.GetUpperBound(0)) = _current

                End Sub


                Public Function HasChildren() As Boolean

                    Return False

                End Function

            End Class

            Public Class DateItem

                Private _attributes As Dictionary(Of String, String)
                Private _deleted As Boolean
                Private _index As Integer
                Private _text As String


                Public Sub New(Index As Integer)

                    _index = Index
                End Sub

                Public ReadOnly Property Attributes As Dictionary(Of String, String)
                    Get
                        If _attributes Is Nothing Then
                            _attributes = New Dictionary(Of String, String)
                        End If
                        Return _attributes
                    End Get
                End Property

                Public ReadOnly Property ListIndex As Integer
                    Get
                        Return _index
                    End Get
                End Property

                Public Property Text As String
                    Get
                        Return _text
                    End Get
                    Set(value As String)
                        _text = value
                    End Set
                End Property

            End Class



            Public Sub New()

                _current = New AlbumItem(0)

            End Sub


            Public Overrides ReadOnly Property Attributes As Dictionary(Of String, String)
                Get
                    Return _current.Attributes
                End Get
            End Property

            Public ReadOnly Property Current As AlbumItem
                Get
                    Return _current
                End Get
            End Property

            Public ReadOnly Property List As AlbumItem()
                Get
                    Return _list
                End Get
            End Property

            Public Overrides Property Text As String
                Get
                    Return _current.Text
                End Get
                Set(value As String)
                    _current.Text = value
                End Set
            End Property


            Public ReadOnly Property Date As DateNode
                Get
                    Return _current.Date
                End Get
            End Property


            Public Overrides Sub Add()

                If _list IsNot Nothing Then
                    ReDim Preserve _list(_list.GetUpperBound(0) + 1)
                Else
                    ReDim _list(0)
                End If


                _current = New AlbumItem(_list.GetUpperBound(0))
                _list(_list.GetUpperBound(0)) = _current

            End Sub


            Public Function HasChildren() As Boolean

                Return False

            End Function

        End Class

        Public Class AlbumItem

            Private _attributes As Dictionary(Of String, String)
            Private _deleted As Boolean
            Private _index As Integer
            Private _text As String
            Private _Date As AlbumNode.DateNode



            Public Sub New(Index As Integer)

                _index = Index
                _Date = New AlbumNode.DateNode


            End Sub

            Public ReadOnly Property Attributes As Dictionary(Of String, String)
                Get
                    If _attributes Is Nothing Then
                        _attributes = New Dictionary(Of String, String)
                    End If
                    Return _attributes
                End Get
            End Property

            Public ReadOnly Property ListIndex As Integer
                Get
                    Return _index
                End Get
            End Property

            Public Property Text As String
                Get
                    Return _text
                End Get
                Set(value As String)
                    _text = value
                End Set
            End Property

            Public ReadOnly Property Date As AlbumNode.DateNode
                Get
                    Return _Date
                End Get
            End Property


        End Class



        Public Sub New()

            _current = New BandItem(0)

        End Sub


        Public Overrides ReadOnly Property Attributes As Dictionary(Of String, String)
            Get
                Return _current.Attributes
            End Get
        End Property

        Public ReadOnly Property Current As BandItem
            Get
                Return _current
            End Get
        End Property

        Public ReadOnly Property List As BandItem()
            Get
                Return _list
            End Get
        End Property

        Public Overrides Property Text As String
            Get
                Return _current.Text
            End Get
            Set(value As String)
                _current.Text = value
            End Set
        End Property


        Public ReadOnly Property Album As AlbumNode
            Get
                Return _current.Album
            End Get
        End Property


        Public Overrides Sub Add()

            If _list IsNot Nothing Then
                ReDim Preserve _list(_list.GetUpperBound(0) + 1)
            Else
                ReDim _list(0)
            End If


            _current = New BandItem(_list.GetUpperBound(0))
            _list(_list.GetUpperBound(0)) = _current

        End Sub


        Public Function HasChildren() As Boolean

            Dim Children As Boolean



            Return Children

        End Function

    End Class

    Public Class BandItem

        Private _attributes As Dictionary(Of String, String)
        Private _deleted As Boolean
        Private _index As Integer
        Private _text As String
        Private _Album As BandNode.AlbumNode



        Public Sub New(Index As Integer)

            _index = Index
            _Album = New BandNode.AlbumNode


        End Sub

        Public ReadOnly Property Attributes As Dictionary(Of String, String)
            Get
                If _attributes Is Nothing Then
                    _attributes = New Dictionary(Of String, String)
                End If
                Return _attributes
            End Get
        End Property

        Public ReadOnly Property ListIndex As Integer
            Get
                Return _index
            End Get
        End Property

        Public Property Text As String
            Get
                Return _text
            End Get
            Set(value As String)
                _text = value
            End Set
        End Property

        Public ReadOnly Property Album As BandNode.AlbumNode
            Get
                Return _Album
            End Get
        End Property


    End Class

    Private _FilePath As String


    Public Sub New()

        _current = New RecordsItem(0)
        _errors = New List(Of String)
        _orphans = New Dictionary(Of String, List(Of XmlClassBase))

    End Sub


    Public Overrides ReadOnly Property Attributes As Dictionary(Of String, String)
        Get
            Return _current.Attributes
        End Get
    End Property

    Public ReadOnly Property Current As RecordsItem
        Get
            Return _current
        End Get
    End Property

    Public ReadOnly Property Errors As List(Of String)
        Get
            Return _errors
        End Get
    End Property

    Public Property FilePath As String
        Get
            Return _FilePath
        End Get
        Set
            _FilePath = value
        End Set
    End Property
    Public ReadOnly Property OrphanNodes As Dictionary(Of String, List(Of XmlClassBase))
        Get
            Return _orphans
        End Get
    End Property

    Public Overrides Property Text As String
        Get
            Return _current.Text
        End Get
        Set(value As String)
            _current.Text = value
        End Set
    End Property


    Public ReadOnly Property Band As BandNode
        Get
            Return _current.Band
        End Get
    End Property

    Public Function Load() As Boolean

        Dim Doc As xmldocument
        Dim Node As XmlNode = Nothing
        Dim r As Integer


        Doc = New XmlDocument()
        Doc.Load(_FilePath)

        For Each Node In Doc.ChildNodes
            If Node.Name <> "xml" Then 'Ignore any declarations.
                Exit For 'Only 1 root node allowed, any others are ignored.
            End If
        Next

        If Node.Attributes IsNot Nothing AndAlso Node.Attributes.Count > 0 Then
            For r = 0 To Node.Attributes.Count - 1
                Attributes.Add(Node.Attributes(r).Name, Node.Attributes(r).InnerText)
            Next
        End If

        LoadChildren(Node, String.Empty, Me)

        Return True

    End Function

    Public Function LoadChildren(xmlCurrent As XmlNode, ParentText As String, Parent As XmlClassBase) As XmlNode

        Dim xmlNodeText As XmlNode
        Dim Destination As XmlClassBase
        Dim r As Integer
        Dim strText As String


        For Each xmlChildNode As XmlNode In xmlCurrent.ChildNodes

            If xmlChildNode.NodeType = XmlNodeType.Element Then

                Try
                    Destination = DirectCast(CallByName(Parent, xmlChildNode.Name, CallType.Get), XmlClassBase)
                Catch mme As MissingMemberException
                    _errors.Add(mme.Message)
                    Destination = New XmlClassBase
                    AddOrphan(xmlChildNode.Name, Destination)
                Catch ex As Exception
                    _errors.Add(ex.Message)
                    Throw
                End Try

                If Destination IsNot Nothing Then
                    If Destination.GetType.ToString <> "Records.XmlClassBase" Then
                        Destination.Add()
                    End If

                    strText = String.Empty

                    For Each xmlNodeText In xmlChildNode
                        If xmlNodeText.NodeType = XmlNodeType.Text Then
                            strText &= xmlNodeText.InnerText.Trim
                        End If
                    Next

                    If strText <> String.Empty Then
                        Destination.Text = strText
                    End If

                    If xmlChildNode.Attributes IsNot Nothing AndAlso xmlChildNode.Attributes.Count > 0 Then
                        For r = 0 To xmlChildNode.Attributes.Count - 1
                            Destination.Attributes.Add(xmlChildNode.Attributes(r).Name, xmlChildNode.Attributes(r).InnerText)
                        Next
                    End If

                    If xmlChildNode.HasChildNodes Then
                        LoadChildren(xmlChildNode, xmlChildNode.Name, Destination)
                    End If

                End If

            End If

        Next

        Return xmlCurrent

    End Function

End Class

Public Class RecordsItem

    Private _attributes As Dictionary(Of String, String)
    Private _deleted As Boolean
    Private _index As Integer
    Private _text As String
    Private _Band As RecordsNode.BandNode



    Public Sub New(Index As Integer)

        _index = Index
        _Band = New RecordsNode.BandNode


    End Sub

    Public ReadOnly Property Attributes As Dictionary(Of String, String)
        Get
            If _attributes Is Nothing Then
                _attributes = New Dictionary(Of String, String)
            End If
            Return _attributes
        End Get
    End Property

    Public ReadOnly Property ListIndex As Integer
        Get
            Return _index
        End Get
    End Property

    Public Property Text As String
        Get
            Return _text
        End Get
        Set(value As String)
            _text = value
        End Set
    End Property

    Public ReadOnly Property Band As RecordsNode.BandNode
        Get
            Return _Band
        End Get
    End Property


End Class

Load方法适用于计算机可以处理的多个xmlElements。我喜欢它,因为它很简洁。我的问题是使用Save方法。我不确定什么是最好的方法。我需要在RecordsNode下获取所有类并将它们写出来为xml。

我可以使用像Assembly.GetExecutingAssembly.GetTypes()这样的东西,但是hbow我会过滤掉所有不属于RecordsNode子类的类吗?

xml可能非常大。我可以为每个类添加代码而不是花哨。这可能是最安全和最丑陋的方法。

还是其他一些选择?客户可以使用此类,因此我无法保证其目的地工作环境。

感谢。

1 个答案:

答案 0 :(得分:0)

XmlSerializer可以更轻松地完成大部分工作。所以使用xsd.exe从你的xml创建一个类,然后用你的数据填充该类,之后序列化这样的对象:

 var o = new MyXmlObject();
 //add data to your object
 //...
 XmlSerializer xs = new XmlSerializer(typeof(MyXmlObject));

 StringWriter sw = new StringWriter();
 using(XmlWriter xw = XmlWriter.Create(sw))
 {
     xs.Serialize(xw, o);
     using(StreamWriter sw = new StreamWriter("filepath"))
         sw.Write(sw.ToString());
 }