需要一个良好的结构来处理XML数据

时间:2016-10-14 16:03:52

标签: c# 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 XmlClassBase


    Friend _list As LinkedList(Of XmlClassListItem)
    Friend _current As LinkedListNode(Of XmlClassListItem)

    Public IncludeIfEmpty As Boolean


    Public ReadOnly Property Attributes() As Dictionary(Of String, String)
        Get
            If _current Is Nothing Then
                _current = _list.AddLast(New XmlClassListItem)
            End If
            Return _current.Value.Attributes
        End Get
    End Property

    Public Property Text As String
        Get
            If _current IsNot Nothing Then
                Return _current.Value.Text
            Else
                Return String.Empty
            End If
        End Get
        Set(value As String)
            If _current Is Nothing Then
                _current = _list.AddLast(New XmlClassListItem)
            End If
            _current.Value.Text = value
        End Set
    End Property

    Friend Sub New()

        _list = New LinkedList(Of XmlClassListItem)
        _current = _list.AddLast(New XmlClassListItem)

    End Sub

    Public Overridable Sub Add()

        Throw New System.Exception("Add called on base " + Me.GetType.ToString + ".")

    End Sub

    Friend Function AddInternal(NewElement As XmlClassListItem) As Boolean

        Dim NewNode As LinkedListNode(Of XmlClassListItem)
        Dim rc As Boolean


        NewNode = _list.AddLast(NewElement)
        If NewNode IsNot Nothing Then
            _current = NewNode
            rc =True
        End If

        Return rc
    End Function

End Class





Public Class XmlClassListItem


    Private _text As String


    Public Attributes As Dictionary(Of String, String)


    Public Sub New()
        Attributes = New Dictionary(Of String, String)
    End Sub

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

End Class

我通过例程运行xml,这就是我的结果:

public class RecordsNode
    inherits XmlClassBase



    public class BandNode
        inherits XmlClassBase



        public class AlbumNode
            inherits XmlClassBase



            public class DateNode
                inherits XmlClassBase


                public Overrides sub Add()

                    dim NewDate as new xmlclasslistitem


                    addinternal(newDate)

                end sub


                public function HasChildren() as boolean

                    return false

                end function
End class

            private _Date as DateNode


            public sub new()

                _Date = new DateNode


            end Sub


        Public ReadOnly Property [Date] As DateNode
            Get
                Return _Date
            End Get
        End Property


        Public Overrides sub Add()

                dim NewAlbum as new xmlclasslistitem


                addinternal(newAlbum)

            end sub


            public function HasChildren() as boolean

                return false

            end function
End class

        private _Album as AlbumNode


        public sub new()

            _Album = new AlbumNode


        end sub


    public readonly property Album as AlbumNode
        get
            return _Album
        end get
    end property


        public Overrides sub Add()

            dim NewBand as new xmlclasslistitem


            addinternal(newBand)

        end sub


        public function HasChildren() as boolean

            dim Children as boolean



            return Children

        end function

End class

    private _Band as BandNode

    private _FilePath as string

    public sub new()

        _Band = new BandNode


    end sub


public readonly property Band as BandNode
    get
        return _Band
    end get
end property


public property FilePath as string
    get
        return _FilePath
    end get
    set
        _FilePath = value
    end set
end property


private sub AddElement(Doc As XmlDocument, ByRef Parent As XmlElement, ParentName As String, Child As XmlElement)


    if Parent is nothing then
        Parent = Doc.CreateElement(ParentName)
    end if

    Parent.AppendChild(Child)

end sub

private function CreateElement(Item as XmlClassBase) as boolean


    if Item.IncludeIfEmpty or Item.Text <> string.empty or Item.Attributes.Count > 0 then
        return true
    else
        return false
    end if

end Function

private function SaveElement(Doc as Xmldocument, Item as xmlclassbase, Parent as xmlelement, strParentName as string, strText as string) as xmlelement

    Dim Attribute As XmlAttribute
    dim Element As XmlElement = nothing
    dim ElementName As string
    Dim KeyValue As KeyValuePair(Of String, String)


    'if CreateElement(Item)
        ElementName = Item.gettype.name
        ElementName = ElementName.substring(0, ElementName.length - 4)
        Element = Doc.CreateElement(ElementName)
        Element.InnerText = strText
        AddElement(Doc, Parent, strParentName, Element)
        For Each KeyValue In Item.Attributes
            Attribute = Doc.CreateAttribute(KeyValue.Key)
            Attribute.Value = KeyValue.Value
            Element.Attributes.Append(Attribute)
        next
    'end if

    return Element

end Function


public function Load() as boolean

    dim Doc as xmldocument


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

    LoadHelper(Doc, "Band", me)
    LoadHelper(Doc, "Band", me.Band)
    LoadHelper(Doc, "Album", me.Band.Album)
    LoadHelper(Doc, "Date", me.Band.Album.Date)

    return true

end function

public sub LoadHelper(Doc As XmlDocument, Source As String, Target As XmlClassBase)

    dim bFirstNode as boolean
    dim lisNodes as XmlNodeList
    dim r as Integer
    dim strText as string = string.empty


    bFirstNode = True
    lisNodes = Doc.GetElementsByTagName(Source)

    For Each Node As XmlNode In lisNodes

        If bFirstNode Then
            bFirstNode = False
        else
            Target.Add()
        end if

        strText = String.Empty

        For Each child As XmlNode In Node.ChildNodes
            If child.NodeType = XmlNodeType.Text Or child.NodeType = XmlNodeType.CDATA Then
                strText &= child.Value.Trim
            end if
        next

        If strText <> String.Empty Then
            Target.Text = strText
        end if

        For r = 0 To Node.Attributes.Count - 1

            Target.Attributes.Add(Node.Attributes(r).Name, Node.Attributes(r).InnerText)

        next

    next

end sub

public function Save() as boolean

    dim Records0 as xmlelement = nothing
    dim Band1 as xmlelement = nothing
    dim Album2 as xmlelement = nothing
    dim Date3 as xmlelement = nothing
    dim Dec as xmldeclaration
    dim Doc as xmldocument


    Doc = New XmlDocument()
    Dec = Doc.CreateXmlDeclaration("1.0", "utf-8", String.Empty)
    Doc.AppendChild(Dec)
    Records0 = SaveElement(Doc, me, nothing, "Records", Text)

    For Each Item As XmlClassListItem In me.Band._list
        Band1 = SaveElement(Doc, me.Band, Records0, "Band", Item.Text)
        Records0.appendchild(Band1)
    next
    For Each Item As XmlClassListItem In me.Band.Album._list
        Album2 = SaveElement(Doc, me.Band.Album, Band1, "Album", Item.Text)
        Band1.appendchild(Album2)
    next
    For Each Item As XmlClassListItem In me.Band.Album.Date._list
        Date3 = SaveElement(Doc, me.Band.Album.Date, Album2, "Date", Item.Text)
        Album2.appendchild(Date3)
    next

    If Band1 IsNot Nothing Then
        Records0.AppendChild(Band1)
    end If
    If Album2 IsNot Nothing Then
        Band1.AppendChild(Album2)
    end If
    If Date3 IsNot Nothing Then
        Album2.AppendChild(Date3)
    end If


    Doc.AppendChild(Records0)
    Doc.Save(_FilePath)

    return true

end Function

    public Overrides sub Add()

        dim NewRecords as new xmlclasslistitem


        addinternal(newRecords)

    end sub


    public function HasChildren() as boolean

        dim Children as boolean



        return Children

    end function

End class

工作得相当好。简单地获取一些数据:

    r = New RecordsNode
    r.FilePath = "C:\Test.xml"
    r.Load()

    TextBox1.Text = r.Band.Text
    TextBox2.Text = r.Band.Album.Text
    TextBox3.Text = r.Band.Album.Date.Text

注意:要测试创建项目,请添加三个文本框并将代码添加到Form_Load事件中。现在将类RecordsNode和Base类添加到一个单独的文件中。最后将XML复制到文件中。

关于这一点的好处是它对开发人员来说很容易,并使代码具有可读性。

添加节点并填充列表框(请注意,新节点仅存在于内存中,但如果调用Save方法,则该节点将保持不变。)

    r = New RecordsNode
    r.FilePath = "C:\Test.xml"
    r.Load()

    r.Band.Add()
    r.Band.Text = "Metallica"

    For Each band As XmlClassListItem In r.Band._list
        ListBox1.Items.Add(band.Text)
    Next

一个主要缺陷是无法访问循环中的子成员。如果我想要循环中的乐队的专辑或专辑,则无法获得。我没有看到解决这个问题的简单方法。

在我从零开始并重新设计整个事情之前,我想知道是否有人能够找到解决方法。

如果有人需要,我可以提供C#版本。

请不要告诉我使用XSD。我试过这个,它使得丑陋的课程变得丑陋。我不认为有人认为有一个名为 BatchPatientDataRequisitionDataOptionalDataUserField 的类使代码可读。

1 个答案:

答案 0 :(得分:0)

我喜欢使用xml的方式:

http://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part

非常简单,直接且干净。它基本上为你完成了所有的工作。