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

摘要:
今天在C#中使用SelectNodes的时候出现了一些怪现象,先从还原现场开始吧。˃Joe17Kate12Parry66Qiqi32David23Eath54下面是我的C#代码staticvoidMain{XmlDocumentdoc=newXmlDocument();doc.Load;XmlElementroot=doc.DocumentElement;XmlNodeuserCollection=root.SelectSingleNode;XmlNodeListusersOfOne=userCollection.SelectNodes;XmlNodeplaceholder=doc.CreateElement;channel.ReplaceChild;Console.WriteLine;}代码逻辑很简单,就是想找到第一个节点把它第一个子节点替换一下。this.done){this.ReadUntil;}returnthis.list.Count;}}它返回的数就是构造函数里创建的List的Count。我们使用SelectNodes的时候,它并没有真正的将节点取出来,而是当我们调用了其它方法后,才通过ReadUntil这个方法将它们的值保存到那个List中。我们修改下上面的代码如下:staticvoidMain{XmlDocumentdoc=newXmlDocument();doc.Load;XmlElementroot=doc.DocumentElement;XmlNodechannel=root.SelectSingleNode;XmlNodeListusersOfOne=channel.SelectNodes;//在SelectNodes之后马上调用CountConsole.WriteLine;XmlNodeplaceholder=doc.CreateElement;channel.ReplaceChild;Console.WriteLine;}它就会像我预期的那样打印出结果了。

今天在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 = newXmlDocument(); 
            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(stringxpath) 
       { 
           XPathNavigator navigator = this.CreateNavigator(); 
           if (navigator == null) 
           { 
               return null; 
           } 
           return newXPathNodeList(navigator.Select(xpath)); 
       }

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

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

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

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

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

public override XmlNode Item(intindex) 
        { 
            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 = newXmlDocument(); 
            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,导致程序在运行的时候和调试的时候表现不同。

免责声明:文章转载自《C#中使用SelectNodes筛选XML元素的问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ARM64架构下,OpenJDK的官方Docker镜像为何没有8版本oracle 在C# 中调用oracle的数据库时,出现引用库和当前客户端不兼容的问题解决方案下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Spring Boot集成MyBatis的2种方式

目录 写在前面 准备工作 配置数据库驱动 配置数据源 原生集成MyBatis 依赖配置 注册MyBatis核心组件 定义并使用映射器 通过MyBatis-Spring-Boot-Starter集成 默认配置 高级定制 总结与比较 写在前面 最近总是有同事和技术群的朋友提问在Spring Boot中使用MyBatis时遇到的问题,大多...

C#使用XML文件记录操作日志

记录应用程序的操作日志可以使用数据库、文本文件、XML文件等。我这里介绍的是使用 XML 文件记录操作日志。我觉得使用 XML 记录操作日志有如下几点好处:1. 不占用数据库的空间,可以任意的删除历史操作日志。2. DataTable 可以方面的读入 XML 文件,DataTable 也可以方便的保存为 XML 文件。3. 查看日志方便,可以直接打开 XM...

XML和JSON数据格式

目录 XML DTD(文档类型定义) DTD实体 JSON 使用python解析JSON数据 XML和JSON的区别  XML的优缺点 JSON的优缺点 XML和JSON都是web存储和传输过程中数据的格式,其中JSON 使用比较广泛,主要用来前后端交互数据。 XML XML(Extensible Markup Language)扩展标记语言 ,是一种常...

Java解析XML

xml报文 <?xml version='1.0' encoding='utf-8'?> <bookstore> <book id='1'> <name>冰与火之歌</name> <author>乔治马丁</author>...

C#实现程序的版本升级更新

我们做了程序,不免会有版本升级,这就需要程序有自动版本升级的功能。 那么看看我是如何实现程序自动更新的。 直接上代码:   view plaincopy to clipboardprint? using System;   using System.Collections.Generic;   using System.Text;   using ...

Android 4.0 SDK的离线方式安装(转)

转自:http://networking.ctocio.com.cn/110/12195610.shtml  作 为一个IT技术人员,迫不及待地就奔向了http://developer.android.com去看看有没有新的SDK公布出来,当时是上午,没见到 有更新,心想一定是若干天后才会发布。没想到同事下午告诉我,新版的SDK已经发布了。当时是临下班,...