代码之家  ›  专栏  ›  技术社区  ›  Brian Surowiec

我的XML代码或.NET中有错误吗?

  •  1
  • Brian Surowiec  · 技术社区  · 14 年前

    我刚刚遇到了一个问题,我的代码正在解析XML fine,但一旦我添加到第二个节点中,它就开始加载不正确的数据。真正的代码跨越了许多类和项目,但是对于这个示例,我已经把导致问题的基本要素放在了一起。

    当代码运行时,我希望输出是第二个任务节点的内容,而不是第一个节点的内容是输出。尽管在检查设置对象时,它的内部XML是第二个任务节点的内部XML,但它始终从第一个出现的emailAddresses节点中提取。呼唤 SelectSingleNode("//EmailAddresses") 是问题发生的地方。

    我有两种方法来解决这个问题

    1. 从emailAddresses xpath表达式中删除前导斜杠
    2. 呼叫 Clone() 获取任务或设置节点后

    解决方案1在这种情况下可以工作,但我相信这会导致我的项目中的其他代码停止工作。

    解决方案2在我看来更像一个黑客,而不是真正的解决方案。

    我的问题是,事实上,我这样做是正确的吗?.NET(所有版本)中有一个bug,还是我只是把XML弄错了?

    C码

    var doc = new XmlDocument();
    doc.Load(@"D:\temp\Sample.xml");
    
    var tasks = doc.SelectSingleNode("Server/Tasks");
    
    foreach (XmlNode threadNode in tasks.ChildNodes)
    {
        if (threadNode.Name.ToLower() != "thread")
        {
            continue;
        }
    
        foreach (XmlNode taskNode in threadNode.ChildNodes)
        {
            if (taskNode.Name.ToLower() != "task" || taskNode.Attributes["name"].Value != "task 1")
            {
                continue;
            }
    
            var settings = taskNode.SelectSingleNode("Settings");
    
            var emails = settings.SelectSingleNode("//EmailAddresses");
    
            Console.WriteLine(emails.InnerText);
        }
    }
    

    XML

    <?xml version="1.0"?>
    <Server>
        <Tasks>
            <Thread>
                <Task name="task 2">
                    <Settings>
                        <EmailAddresses>task 2 data</EmailAddresses>
                    </Settings>
                </Task>
            </Thread>
            <Thread>
                <Task name="task 1">
                    <Settings>
                        <EmailAddresses>task 1 data</EmailAddresses>
                    </Settings>
                </Task>
            </Thread>
        </Tasks>
    </Server>
    
    2 回复  |  直到 14 年前
        1
  •  5
  •   user357812    14 年前

    http://www.w3.org/TR/xpath/#path-abbrev

    // 是短的 /descendant-or-self::node()/ . 为了 例子, //para 是短的 /descendant-or-self::node()/child::para 因此将在 文档(即使是para元素, 是否将选择文档元素 通过 //帕拉 因为document元素 节点是根节点的子节点);

    还有:

    位置步骤 . 是短的 self::node() . 这是特别的 与一起使用 / / . 为了 例如,位置路径 .//para 是短的

    self::node()/descendant-or-self::node()/child::para
    

    因此将选择所有的para后代 上下文节点的元素。

    而不是:

    var settings = taskNode.SelectSingleNode("Settings");
    
    var emails = settings.SelectSingleNode("//EmailAddresses");
    

    用途:

    var emails = taskNode.SelectSingleNode("Settings/EmailAddresses");
    
        2
  •  3
  •   Frédéric Hamidi    14 年前

    这个 // XPath expression 不会像你想象的那样去做。它 selects nodes in the document from the current node that match the selection no matter where they are .

    换句话说,它不受当前范围的限制,它实际上会爬回文档树并从根元素开始匹配。

    选择第一个 <EmailAddresses> 元素在当前作用域中,您只需要:

    var emails = settings.SelectSingleNode("EmailAddresses");