将XML结构重建为递归样式列表/ XMLReader替代方案

时间:2017-09-06 11:23:18

标签: c# .net xml xml-parsing

问题:

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

问题:

由于我的xml有任何顺序的节点,有些是其他节点的子节点,我在努力逻辑解析它们而不使用.net 1.1和XmlNode类。

注意:我希望只使用XMLReader,因为我被限制为.Net Standard 1.0,并且不想安装任何额外的库。 (见这里:https://docs.microsoft.com/en-us/dotnet/standard/net-standard

目前,我为每个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

1 个答案:

答案 0 :(得分:3)

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

将其付诸实践,将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;
    }