草庐IT

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

coder 2024-06-30 原文

问题:

我一直在尝试解析 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# - 将 XML 结构重建为递归样式列表/XMLReader 替代方案的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  3. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - RVM 使用列表[0] - 2

    是否有类似“RVMuse1”或“RVMuselist[0]”之类的内容而不是键入整个版本号。在任何时候,我们都会看到一个可能包含5个或更多ruby的列表,我们可以轻松地键入一个数字而不是X.X.X。这也有助于rvmgemset。 最佳答案 这在RVM2.0中是可能的=>https://docs.google.com/document/d/1xW9GeEpLOWPcddDg_hOPvK4oeLxJmU3Q5FiCNT7nTAc/edit?usp=sharing-知道链接的任何人都可以发表评论

  6. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  7. ruby-on-rails - 更好的替代方法 try( :output). try( :data). try( :name)? - 2

    “输出”是一个序列化的OpenStruct。定义标题try(:output).try(:data).try(:title)结束什么会更好?:) 最佳答案 或者只是这样:deftitleoutput.data.titlerescuenilend 关于ruby-on-rails-更好的替代方法try(:output).try(:data).try(:name)?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

  8. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  9. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  10. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

随机推荐