需要一个良好的结构来操作XML数据

本文关键字:结构 操作 数据 XML 一个 | 更新日期: 2023-09-27 18:13:02

我试图提出一个很好的类结构来简化处理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和基类。最后将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使代码可读。

需要一个良好的结构来操作XML数据

我喜欢使用的处理xml的方式:

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

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