【问题标题】:How to build XML Tree in c# with the available structure of XML Tree如何在 C# 中使用 XML Tree 的可用结构构建 XML Tree
【发布时间】:2020-02-24 16:47:11
【问题描述】:

我有一个'List(Of String)'的路径列表,我想相应地创建xml树。

例如:假设我有 10 条路径如下

  1. a/b.book
  2. a/b/c.book
  3. a/b/c/d/e.page
  4. a/b/c/d/f.page
  5. a/b/g.book
  6. a/b/g/h/i.page
  7. a/b/g/h/j.page
  8. k/l.book
  9. k/l/m/n.page
  10. o/p.book

我的预期输出:

<?xml version="1.0" encoding="UTF-8"?>
<map>
<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>
</book>
</map>

【问题讨论】:

    标签: c# xml vb.net list path


    【解决方案1】:

    使用 Xml Linq 尝试以下递归算法

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    using System.Net;
    
    
    namespace ConsoleApplication157
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                string[] inputs = {
                                      "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"
                                  };
                List<List<string>> splitArrays = inputs.Select(x => x.Split(new char[] { '/', '.' }).ToList()).ToList();
    
                XElement root = new XElement("root");
                GetTree(root, splitArrays);
    
            }
            static void GetTree(XElement parent, List<List<string>> splitArrays)
            {
                var groups = splitArrays.OrderBy(x => x[0]).GroupBy(x => new { path = x.First(), type = x.Last() }).ToArray();
                foreach (var group in groups)
                {
                    List<List<string>> children = null;
                    XElement element = new XElement(group.Key.type, new XAttribute("navtitle", group.Key.path));
                    parent.Add(element);
                    Boolean first = true;
                    foreach (var child in group.OrderByDescending(x => x.Count))
                    {
                        if (child.Count() == 2) //since we sorts by count, 1 indicates we are at the leaf
                        {
                            if (first)
                            {
                                if (children != null)
                                {
                                    GetTree(element, children);
                                    children = null;
                                }
                                first = false;
                            }
                        }
                        else
                        {
                            //remove first index of each splitArray
                            if (children == null) children = new List<List<string>>();
                            List<string> newChild = child.Skip(1).ToList();
                            children.Add(newChild);
                        }
    
                    }
                    //when there are no elements with count = 1 then call Getree here
                    if (children != null)
                    {
                        GetTree(element, children);
                    }
    
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      它可能看起来像这种递归方法。

      Private Sub BuildTrie(path As String,
                            trie As XElement)
          Dim p As List(Of String)
          p = path.Split(New Char() {"/"c}, StringSplitOptions.RemoveEmptyEntries).ToList
          If p.Count > 0 Then
              Dim thisP As String = p(0)
              If thisP <> "" Then
                  p.RemoveAt(0)
                  Dim ie As IEnumerable(Of XElement)
                  ie = From el In trie.Elements Where el.@navtitle = thisP Select el Take 1
      
                  Dim thisND As XElement
                  If ie.Count = 1 Then
                      thisND = ie(0)
                  Else
                      thisND = <book navtitle=""></book>
                      thisND.@navtitle = thisP
                      trie.Add(thisND)
                  End If
                  If p.Count > 0 Then BuildTrie(String.Join("/"c, p), thisND)
              End If
          End If
      End Sub
      

      要看看它是如何工作的,试试这个

          Dim pths() As String = {"a/b/c", "a/b/c/d/f", "a/b/g", "a/b/g/h", "k/l", "k/l/m/n", "o/p"}
          Dim _trie As XElement = <books></books>
          For Each p As String In pths
              BuildTrie(p, _trie)
          Next
      
          Stop ' look at _trie
      

      这还不完整,但也许它会给你一些想法。

      编辑:上面的输出。

      <books>
        <book navtitle="a">
          <book navtitle="b">
            <book navtitle="c">
              <book navtitle="d">
                <book navtitle="f"></book>
              </book>
            </book>
            <book navtitle="g">
              <book navtitle="h"></book>
            </book>
          </book>
        </book>
        <book navtitle="k">
          <book navtitle="l">
            <book navtitle="m">
              <book navtitle="n"></book>
            </book>
          </book>
        </book>
        <book navtitle="o">
          <book navtitle="p"></book>
        </book>
      </books>
      

      【讨论】:

        【解决方案3】:

        你评论说你是"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 拥有与我们之前创建并写入文件的完全相同的地图。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-29
          • 2015-05-08
          • 1970-01-01
          • 2017-09-16
          • 2023-03-17
          • 2010-09-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多