程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中使用SelectNodes篩選XML元素的問題

C#中使用SelectNodes篩選XML元素的問題

編輯:C#入門知識

今天在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,導致程序在運行的時候和調試的時候表現不同。

 

 

摘自 大眾.NET

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved