• [C#] XmlDocument 搭配 Linq 與 XPath


    一般最常搭配 XML 用來查詢資料的技術是 XPath 。不過 .Net 僅支援到 XPath 1.0 ,有時想在 XPath 中加上日期函式的判斷都不行。
    再加上若使用了 XPath 的函式或一些判斷式後,整個 XmlDocument 的查詢效能會變慢。(參考以下的例子)

    本文章所使用的 XML 內容 (ad.xml :紀錄一些廣告圖與連結):

    01 <?xml version="1.0" encoding="utf-8" ?>
    02 <ads>
    03   <item>
    04     <title>我的E政府</title>
    05     <weight>10</weight>
    07     <url>http://www.gov.tw/%3C/url>
    08   </item>
    09   <item>
    10     <title>PChome Online 網路家庭</title>
    11     <weight>9</weight>
    13     <url>http://www.pchome.com.tw/%3C/url>
    14   </item>
    15   <item>
    16     <title>Yahoo!奇摩</title>
    17     <weight>9</weight>
    19     <url>http://tw.yahoo.com/%3C/url>
    20   </item>
    21   <item>
    22     <title>巴哈姆特電玩資訊站</title>
    23     <weight>9</weight>
    25     <url>http://www.gamer.com.tw/%3C/url>
    26   </item>
    27 </ads>

    假如我們現在要找出所有 <weight> 值為 9 的 <item> ,傳統上可用以下兩個方式:
    (程式部分用迴圈跑 10000 次,來區分兩種做法的效能)

    1. 使用 XPath 做篩選:
      01 XmlDocument doc = new XmlDocument();
      02 doc.Load("ad.xml");
      03 DateTime dtStart = DateTime.Now;
      04 for (int i = 0; i < 10000; i++)
      05 {
      06     XmlNodeList nodes = doc.SelectNodes("//item[weight='9']");
      07     foreach (XmlNode node in nodes)
      08     {
      09         string s = node.OuterXml;
      10     }
      11 }
      12 Console.WriteLine(("{0} (毫秒)" + DateTime.Now - dtStart).Milliseconds);
      13 //輸出了: 337 (毫秒)
    2. 在程式中判斷:
      01 XmlDocument doc = new XmlDocument();
      02 doc.Load("ad.xml");
      03 DateTime dtStart = DateTime.Now;
      04 for (int i = 0; i < 10000; i++)
      05 {
      06     XmlNodeList nodes = doc.SelectNodes("//item");
      07     foreach (XmlNode node in nodes)
      08     {
      09         if (node["weight"].InnerText == "9")
      10         {
      11             string s = node.OuterXml;
      12         }
      13     }
      14 }
      15 Console.WriteLine(("{0} (毫秒)" + DateTime.Now - dtStart).Milliseconds);
      16 //輸出了: 250 (毫秒)


    兩種做法的不同點只有在 SelectNodes 的地方,前者直接透過 XPath 篩選掉不符條件的節點;後者是將 <item> 全取出後,在程式中判斷。 
    儘管效能上有差異,還是可以視情況選擇 XPath 使用的方式。例如僅單純地使用樹狀結構查詢所需的節點,並避免在 XPath 中使用一些函式去做文字或屬性值的判斷。

    自從 .Net 出了 Linq 後,個人感覺對 XML 的查詢更有幫助了。(效能部分因為是端看怎麼查詢,較為主觀,這邊就不比較效能)
    以上述的例子來看,我們可以用 Linq 的語法進行查詢: (僅供參考:效能大約介於上述兩個做法,300毫秒上下)

    1 XmlDocument doc = new XmlDocument();
    2 doc.Load("ad.xml");
    3 var query = from n in doc.SelectNodes("//item").Cast<XmlNode>()
    4             where int.Parse(n["weight"].InnerText) == 9
    5             select n;
    6 foreach (XmlNode node in query)
    7 {
    8     string s = node.OuterXml;
    9 }


    此用法的關鍵在於要將 SelectNodes 做一次 Cast ,轉為 XmlNode 。

    接下來用一個例子來說明 XmlDocument + Linq 的好處。
    假設本文所使用的 ad.xml 檔案是一個網站用來呈現廣告的資料來源,在前台會顯示的廣告圖共有三個,其中權重 (<weight>) 為10的是一定要呈現的廣告,若權重相同,則以亂數決定。
    基於少述的條件,可以大概知道查詢上除了權重 (<weight>)外,還要加上一個隨機的查詢條件,以造成圖片有隨時更換的效果。
    如果用過去的查詢方式,大概會是如下的步驟:

    1. 先查出權重最高的並以亂數排序:將權重為10的 <item> 再搭配上一個亂數進行排序,然後將前三名取出。
    2. 如果權重為10的項目未超過三個,需再從權重小於10的項目中再選出,選的過程還是要加入隨機值做排序。
    3. 一直重覆第2步,直到滿足廣告所需出現的數量 (ex: 三個) 。

    感覺上用資料庫的 SQL 句就可以很容易達成的事 (ORDER BY weight DESC, NEWID() DESC),到了 XML 就因為多了一個隨機亂數,而變得複雜。

    現在,換用 Linq 做,程式碼大致如下:

    01 //廣告最多3則
    02 int intAdCount = 3;
    03 //產生亂數的物件
    04 Random rnd = new System.Random((int)DateTime.Now.Ticks);
    05 XmlDocument doc = new XmlDocument();
    06 doc.Load("ad.xml");
    07 var query = (from n in doc.DocumentElement.SelectNodes(".//item").Cast<XmlNode>()
    08              orderby Convert.ToInt32(n["weight"].InnerText) descending, rnd.Next(0, 10) descending
    09              select n).Take(intAdCount);
    10 foreach (XmlNode node in query)
    11 {
    12     string strTitle = node["title"].InnerText;
    13     string strImage = node["image"].InnerText;
    14     string strUrl = node["url"].InnerText;
    15 }

    上述的程式碼除了將 XML 中排序與亂數的問題解決,甚至利用 Take() 就將最多只抓取三個廣告項這個條件也做掉了。
    由於在 Linq  中可以用到 .Net Framework的函式庫,所以像是日期或字串的判斷,也就顯得更為彈性。
    以上供參考,例子舉得不好請見諒。

    PS. 上述的例子,將第7行後的程式以迴圈方式連續執行10000次,大約花200毫秒,效能上並不會因為用了 Linq 而不好。

  • 相关阅读:
    php-基于面向对象的MySQL类
    php-迭代创建级联目录
    php-删除非空目录
    php-递归创建级联目录
    linux 用户管理
    mysql 语法大全
    dos命令下修改mysql密码的方法
    对 linux init.d的理解
    linux 重启服务器命令
    校验软件包
  • 原文地址:https://www.cnblogs.com/timy/p/1738441.html
Copyright © 2020-2023  润新知