重建XML结构成一个递归样式列表/ XMLReader的替代

重建XML结构成一个递归样式列表/ XMLReader的替代

问题描述:

问题:重建XML结构成一个递归样式列表/ XMLReader的替代

我一直在试图解析XML,并在XML的每个节点创建一个对象。

问题:

由于我的XML已经以任意顺序的节点,有些是其他节点的子节点,我挣扎着逻辑上分析它们不使用.NET 1.1和XmlNode的类。

注意:我希望只使用XMLReader,因为我仅限于.Net Standard 1.0,并且不想安装任何额外的库。

目前,我为每个xml节点创建一个对象,每个对象都包含一个我希望添加到的子组件列表,如果它找到了一个子节点。不过,我似乎无法递归搜索xml,并正确地生成列表/列表的结构。

我的代码

using System.IO; 
using System.Xml; 
using System.Xml.Linq; 

namespace ConsoleApp1 
{ 
    class Program 
    { 
    static List<UIComponent> components = new List<UIComponent>(); 

    static void Main(string[] args) 
    { 
     String path = "C:\\Users\\admin\\Desktop\\test.xml"; 
     parseXML(path); 
    } 


    private static UIComponent addToLowestChild(UIComponent parent, UIComponent child) 
    { 
     for(int i=0;i<parent.getChildren().Count;++i) 
     { 

      if (parent.getChildren().Count > 0) 
      { 
       foreach (UIComponent kid1 in parent.getChildren()) 
       { 
        if (kid1.getChildren().Count > 0) 
        { 
         addToLowestChild(kid1, child); 
        } 
       } 
      } 
     } 
     return parent; 
    } 

    private static UIComponent ChildTest(int depth,UIComponent parent,UIComponent child) 
    { 
     //i=depth for item 4, if i is set to 1 first 
     for (int i = 1; i < depth; ++i) 
      { 
       if (parent.getChildren().Count > 0) 
       { 
        if (i > 1) 
        { 
        ChildTest(i, parent.getChildren()[parent.getChildren().Count - 1], child); 
        } 
        else 
        { 
        parent.getChildren()[parent.getChildren().Count - 1].addChild(child); 
        break; 
        } 
       } 
       else 
       { 
        parent.addChild(child); 
        break; 
       } 
      } 


     return parent; 
    } 

    private static void parseXML(string path) 
    { 
     //read the xml file into one string 
     string[] lines = System.IO.File.ReadAllLines(path); 
     string xmlContent = ""; 
     foreach (string line in lines) 
     { 
      xmlContent += line; 
     } 

     UIComponent currentComponent = null; 

     //parse the xml content 
     using (XmlReader reader = XmlReader.Create(new StringReader(xmlContent))) 
     { 

      while (reader.Read()) 
      { 

       int currentDepth = 0; 
       // Console.WriteLine(reader.Name+" depth:"+reader.Depth); 
       if (reader.Depth > 0) //ignore ground level elements such as <XML> and <UI> 
       { 


        switch (reader.NodeType) 
        { 
         case XmlNodeType.Element: 
          if (currentComponent == null || reader.Depth==currentDepth) 
          { 
           currentComponent = new UIComponent(reader.Name); 
          } 
          else 
          { 
           UIComponent childComponent = new UIComponent(reader.Name); 
           //currentComponent.addChild(childComponent); 
           ChildTest(reader.Depth,currentComponent,childComponent); 
          } 
          break; 

         case XmlNodeType.Text: 
          break; 
         case XmlNodeType.XmlDeclaration: 
         case XmlNodeType.ProcessingInstruction: 
          break; 
         case XmlNodeType.Comment: 
          break; 
         case XmlNodeType.EndElement: 
          if (reader.Depth == 1 && currentComponent!=null) 
          { 
           components.Add(currentComponent); 
           currentComponent = null; 
          } 
          break; 
         default: break; 
        } 
       } 

      } 
     } 
    } 

我的测试数据

<?xml version="1.0" encoding="UTF-8"?> 
<UI> 
    <window x="5"> 
    <Button2 value=""> 
     <Button3 y=""> 
     </Button3> 
     <Button4> 
      <Button6 value=""></Button6> 
      <Button5 value=""></Button5> 
     </Button4> 
    </Button2> 
    </window> 
    <window></window> 
    <heading></heading> 
</UI> 

输出:

-Window 
    |-Button 2 
    |-- Button 3 
    |-- Button 4 
    |-- Button 6 
    |-- Button 5 
-Window 
-Heading 

我想要什么:

-Window 
    |-Button 2 
    |-- Button 3 
    |-- Button 4 
     |-- Button 6 
     |-- Button 5 
-Window 
-Heading 
+0

这里从XmlReader中的描述是:“*代表读者它提供对XML数据的快速,非缓存,只前向访问*“。什么让你认为你实际上可以添加到它? –

+0

什么是* .net 1.0 *? –

+1

我更新了这个问题,以便为将来的读者解决这个问题。谢谢你的帮助@IvanStoev! – D3181

您只需要维护当前处理节点的堆栈。每次你点击StartElement时,你都会创建一个新节点,读取属性,将它添加到父节点或根节点列表中,如果它不是空元素(如<window />),则将它推入堆栈。当你点击EndElement时,你只需弹出(移除)堆栈的最后一个元素。栈顶元素表示当前正在处理的节点。

付诸行动,解析XML像这样简单的类的列表:

class Node 
{ 
    public string Name; 
    public List<Node> Children = new List<Node>(); 
    public Dictionary<string, string> Attributes = new Dictionary<string, string>(); 
    public override string ToString() => Name; 
} 

可能是这样的:

static List<Node> ParseXML(string xmlContent) 
{ 
    using (var reader = XmlReader.Create(new StringReader(xmlContent))) 
    { 
     var rootNodes = new List<Node>(); 
     var nodeStack = new Stack<Node>(); 
     while (reader.Read()) 
     { 
      switch (reader.NodeType) 
      { 
       case XmlNodeType.Element: 
        var node = new Node { Name = reader.Name }; 
        if (reader.MoveToFirstAttribute()) 
        { 
         // Read the attributes 
         do 
         { 
          node.Attributes.Add(reader.Name, reader.Value); 
         } 
         while (reader.MoveToNextAttribute()); 
         // Move back to element 
         reader.MoveToElement(); 
        } 
        var nodes = nodeStack.Count > 0 ? nodeStack.Peek().Children : rootNodes; 
        nodes.Add(node); 
        if (!reader.IsEmptyElement) 
         nodeStack.Push(node); 
        break; 

       case XmlNodeType.EndElement: 
        nodeStack.Pop(); 
        break; 
      } 
     } 
     return rootNodes; 
    }