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

标签 c# .net xml xml-parsing

问题:

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

问题:

由于我的 xml 具有任意顺序的节点,并且有些节点是其他节点的子节点,因此我很难在不使用 .net 1.1 和 XmlNode 类的情况下从逻辑上解析它们。

注意:我希望只使用 XMLReader,因为我仅限于 .Net Standard 1.0,并且不想安装任何额外的库。 (参见此处:https://learn.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

最佳答案

您只需要维护一个当前正在处理的节点的堆栈。任何时候你点击 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;
    }

关于c# - 将 XML 结构重建为递归样式列表/XMLReader 替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46073941/

相关文章:

c# - 在现有 C# 代码中发现的 Dispose。我应该摆脱它吗?

.net - 我可以从闪存驱动器运行 Visual Studio 2010 吗?

java - 谁能解释这里的美元符号是什么意思?

.net - 用于区分大小写的字符串比较的 Resharper 模式

java - 如何从 Android Studio 中 fragment 的 XML 布局导航到相应的 java 类?

mysql - 从备份中恢复 Magento 静态 block 内容?

C# - 使用表单而不是消息框

c# - 在 session 中存储登录名和密码哈希是否安全?

c# - DataContractSerializer 不反序列化引用

c# - 为什么 AWS Lambda 环境中的 EPPlus Excel 库会抛出 "The type initializer for ' Gdip' 抛出异常”