你评论说你是"Trying to build xml tree without usage of xmlelement or xmldocument",我很感激。我想提交另一个选项,即 .NET 类和 Xml 序列化。
首先,我们首先创建可以代表您的数据的类。由于是简单的设计,所以类很简单
Imports System.Xml.Serialization
<XmlRoot("map")>
Public Class book
<XmlAttribute> Public Property navtitle As String
<XmlElement("book")> Public books As List(Of book)
<XmlElement("page")> Public pages As List(Of page)
End Class
Public Class page
<XmlAttribute> Public Property navtitle As String
End Class
为了演示如何手动构建类,这里有一些代码,它们都在一行中,虽然难以阅读,但应该很容易看出它与你的 xml 文件的匹配度
Private Function createMap() As book
Dim m As New book() With {
.books = New List(Of book)() From {
New book() With {.navtitle = "a",
.books = New List(Of book)() From {
New book() With {.navtitle = "b",
.books = New List(Of book)() From {
New book With {.navtitle = "c",
.books = New List(Of book)() From {
New book With {.navtitle = "d",
.pages = New List(Of page)() From {
New page With {.navtitle = "e"},
New page With {.navtitle = "f"}}}}}}}}},
New book() With {.navtitle = "g",
.books = New List(Of book)() From {
New book() With {.navtitle = "h",
.pages = New List(Of page)() From {
New page() With {.navtitle = "i"},
New page() With {.navtitle = "j"}}}}},
New book With {.navtitle = "k",
.books = New List(Of book)() From {
New book() With {.navtitle = "l",
.books = New List(Of book)() From {
New book() With {.navtitle = "m",
.pages = New List(Of page)() From {
New page With {.navtitle = "n"}}}}}}},
New book With {.navtitle = "o",
.books = New List(Of book)() From {
New book() With {.navtitle = "p"}}}}}
Return m
End Function
此函数返回一个包含所有数据的对象,具有强类型。这个对象可以简单地通过 Xml 序列化写入一个 Xml 文件
Private Sub createXmlFile(path As String, b As book)
Dim s As New XmlSerializer(GetType(book))
Using sw As New StreamWriter(path)
s.Serialize(sw, b)
End Using
End Sub
Dim m = createMap()
createXmlFile("path.xml", m)
所以我们有您的类的框架,并将它们写入 Xml,但没有动态解释器。这是一个解释器
Private Function createMap(titles As IEnumerable(Of String)) As book
Dim root As New book()
For Each title In titles
Dim book = root
Dim parts = title.Split("/"c)
For Each part In parts
Dim b As book
If part.Contains(".") Then
If part.Contains("page") Then
If book.pages Is Nothing Then book.pages = New List(Of page)()
book.pages.Add(New page() With {.navtitle = part.Split("."c).First()})
Else
If book.books Is Nothing Then book.books = New List(Of book)()
book.books.Add(New book() With {.navtitle = part.Split("."c).First()})
End If
Else
If book.books?.Any(Function(x) x.navtitle = part.First()) Then
b = book.books.Single(Function(x) x.navtitle = part.First())
Else
b = New book() With {.navtitle = part.First()}
If book.books Is Nothing Then book.books = New List(Of book)()
book.books.Add(b)
End If
book = b
End If
Next
Next
Return root
End Function
请注意,它不是递归的,但如果您愿意,可以使用递归来编写,但这不是必需的。现在我们可以改为调用该函数重载,并传入您的路径。我们使用IEnumerable(Of String) 而不是 List,因为您几乎应该只在您打算对其进行修改时使用 List,例如重新排序。另外,IEnumerable(Of String) 将接受许多不同的类型 - 例如下面的数组
Dim m = createMap(
{"a/b.book",
"a/b/c.book",
"a/b/c/d/e.page",
"a/b/c/d/f.page",
"a/b/g.book",
"a/b/g/h/i.page",
"a/b/g/h/j.page",
"k/l.book",
"k/l/m/n.page",
"o/p.book"})
createXmlFile("path1.xml", m)
您的文件已创建
<?xml version="1.0" encoding="utf-8"?>
<map xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<book navtitle="a">
<book navtitle="b">
<book navtitle="c">
<book navtitle="d">
<page navtitle="e" />
<page navtitle="f" />
</book>
</book>
<book navtitle="g">
<book navtitle="h">
<page navtitle="i" />
<page navtitle="j" />
</book>
</book>
</book>
</book>
<book navtitle="k">
<book navtitle="l">
<book navtitle="m">
<page navtitle="n" />
</book>
</book>
</book>
<book navtitle="o">
<book navtitle="p" />
</book>
</map>
奖励:Xml 序列化还使读取 Xml 文件更加容易
Private Function readXmlFile(path As String) As book
Dim b As book
Dim s As New XmlSerializer(GetType(book))
Using sr As New StreamReader(path)
b = DirectCast(s.Deserialize(sr), book)
End Using
Return b
End Function
Dim m = readXmlFile("path1.xml")
而m 拥有与我们之前创建并写入文件的完全相同的地图。