• C#中使用SelectNodes筛选XML元素的问题


    今天在C#中使用SelectNodes的时候出现了一些怪现象,先从还原现场开始吧。

    首先创建一个简单的XML文件来试验,还是就保存为test.xml

    <?xml version="1.0" encoding="utf-8" ?> 
    <root> 
      <users job="salas"> 
        <user> 
          <name>Joe</name> 
          <age>17</age> 
        </user> 
        <user> 
          <name>Kate</name> 
          <age>12</age> 
        </user> 
        <user> 
          <name>Parry</name> 
          <age>66</age> 
        </user> 
        <user> 
          <name>Qiqi</name> 
          <age>32</age> 
        </user> 
      </users> 
      <users job="developer"> 
        <user> 
          <name>David</name> 
          <age>23</age> 
        </user> 
        <user> 
          <name>Eath</name> 
          <age>54</age> 
        </user> 
      </users> 
    </root> 

    下面是我的C#代码

    static void Main(string[] args) 
            { 
                XmlDocument doc = new XmlDocument(); 
                doc.Load("test.xml");
    
                XmlElement root = doc.DocumentElement; 
                XmlNode userCollection= root.SelectSingleNode("users[1]"); 
                XmlNodeList usersOfOne = userCollection.SelectNodes("user");
    
    
                XmlNode placeholder=doc.CreateElement("placeholder"); 
                channel.ReplaceChild(placeholder, usersOfOne.Item(0));
    
                Console.WriteLine(usersOfOne.Count);
    
    }

    代码逻辑很简单,就是想找到第一个<users>节点把它第一个<user>子节点替换一下。

    关键在于替换之后的问题来了,我原本想的是usersOfOne的个数应该保存着4个<user>节点,但是最终的结果只有1个,而且就只是那个被替换掉的那个节点。

     

    继续试验,这次修改下C#代码,将替换的节点变成第二个<user>节点试试?usersOfOne的个数就变成两个,包括第一和第二个节点。

    研究SelectNodes源码(以下源代码都是在Reflector 6中查看的

    public XmlNodeList SelectNodes(string xpath) 
           { 
               XPathNavigator navigator = this.CreateNavigator(); 
               if (navigator == null) 
               { 
                   return null; 
               } 
               return new XPathNodeList(navigator.Select(xpath)); 
           }

    发现它返回一个XPathNodeList,再去看下它的构造函数

    public XPathNodeList(XPathNodeIterator nodeIterator) 
    { 
        this.nodeIterator = nodeIterator; 
        this.list = new List<XmlNode>(); 
        this.done = false; 
    }

    你会发现它创建了一个List<XmlNode>,但是并没有给它赋值。让我们再去看看Count这个属性

    public override int Count 
           { 
               get 
               { 
                   if (!this.done) 
                   { 
                       this.ReadUntil(0x7fffffff); 
                   } 
                   return this.list.Count; 
               } 
           } 

    它返回的数就是构造函数里创建的List<XmlNode>的Count。再去看看Item()这个函数

    public override XmlNode Item(int index) 
            { 
                if (this.list.Count <= index) 
                { 
                    this.ReadUntil(index); 
                } 
                if ((index >= 0) && (this.list.Count > index)) 
                { 
                    return this.list[index]; 
                } 
                return null; 
            }

    同样的也是返回的List<XmlNode>中的值。

    所以,我们可以解释上面实验代码中的怪现象了。

    我们使用SelectNodes的时候,它并没有真正的将节点取出来,而是当我们调用了其它方法后(比如item()或者Count属性),才通过ReadUntil这个方法将它们的值保存到那个List<XmlNode>中。

    Count这个属性能将0x7fffffff个节点保存下来(这也暗示我们最多能处理的节点个数!?),而Item这个函数只是把你需要的个数保存下来,(大家也可以去看看ReadUntil方法)后面因为我将现在的这个节点替换了,所以在Count的时候,它无法去迭代找到下个节点,所以在替换第二个节点的时候只保留下第一第二节点的原因。

    我们修改下上面的代码如下:

    static void Main(string[] args) 
            { 
                XmlDocument doc = new XmlDocument(); 
                doc.Load("test.xml");
    
                XmlElement root = doc.DocumentElement; 
                XmlNode channel = root.SelectSingleNode("users[1]"); 
                XmlNodeList usersOfOne = channel.SelectNodes("user");
    
                //在SelectNodes之后马上调用Count 
                Console.WriteLine(usersOfOne.Count);
    
    
                XmlNode placeholder=doc.CreateElement("placeholder"); 
                channel.ReplaceChild(placeholder, usersOfOne.Item(0));
    
                Console.WriteLine(usersOfOne.Count);
    
    }

    它就会像我预期的那样打印出结果了。尽管替换节点之后,输出的依然是4。

    这个问题在调试的时候也比较难发现,因为你调试时查看usersOfOne.Count属性,相当于在源程序中执行了Count一样,所以在调试程序的时候,它会输出的结果也是4,导致程序在运行的时候和调试的时候表现不同。

  • 相关阅读:
    Win10 iot 配置防火墙限制应用部署
    未能加载文件或程序集“********”或它的某一个依赖项。试图加载格式不正确的程序。
    IIS 支持 m3u8
    UWP WebView 禁用缩放
    Code First
    关于 永恒之蓝 和 MS17-010 补丁
    《 罗辑思维 成大事者不纠结》读书笔记
    <王川自选集第一卷电子书 >读书笔记
    <王二的经济学故事>读书笔记
    <以交易为生>读书笔记
  • 原文地址:https://www.cnblogs.com/heqichang/p/2517871.html
Copyright © 2020-2023  润新知