我有一个代码生成器来构建基于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可能非常大。我可以为每个类添加代码而不是花哨。这可能是最安全和最丑陋的方法。
还是其他一些选择?客户可以使用此类,因此我无法保证其目的地工作环境。
感谢。
答案 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());
}