Вы прокомментировали, что вы "Trying to build xml tree without usage of xmlelement or xmldocument"
, что я могу оценить. Я хотел бы предоставить еще один вариант: NET classes и Xml Serialization.
Сначала мы начнем с создания классов, которые могут представлять ваши данные. Так как это простой дизайн, классы просты
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, но без динамических c переводчик. Вот интерпретатор
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)
вместо списка, потому что вы почти должны использовать список только тогда, когда вы хотите изменить его, например, изменить порядок. Кроме того, 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
содержит ту же карту, которую мы ранее создали и записали в файл.