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
&
补充:软件开发 , C# ,