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

时间:2010-10-29 19:01:49

标签: c# .net xml xpath

我遇到了一个问题,我的代码解析xml很好但是一旦我在第二个节点中添加它就开始加载不正确的数据。真正的代码涵盖了许多类和项目,但是对于样本我已经汇总了导致问题的基础知识

当代码运行时,我希望输出是第二个Task节点的内容,而是输出第一个节点的内容。尽管如何检查设置对象,其内部xml是第二个Task节点的内部xml,它仍然会从第一次出现的EmailAddresses节点开始拉出。拨打SelectSingleNode("//EmailAddresses")是问题发生的地方。

我有两种解决此问题的方法

  1. 从EmailAddresses XPath表达式中删除前导斜杠
  2. 获取任务或设置节点后调用Clone()
  3. 解决方案1适用于这种情况,但我相信这会导致我的项目中的其他代码停止工作。

    对于我而言,解决方案2看起来更像是一个真正的解决方案。

    我的问题是我实际上是在正确地执行此操作并且.NET(所有版本)中存在错误,或者我只是将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 个答案:

答案 0 :(得分:5)

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

  

//是简称   /descendant-or-self::node()/。对于   例如,//para是简称   /descendant-or-self::node()/child::para   因此将选择任何para元素   该文件(甚至是一个para元素   是一个将被选中的文档元素   自文档元素以来//para   node是根节点的子节点;;

还有:

  

.的位置步骤是简称   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");

答案 1 :(得分:3)

// 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");