再加上若使用了 XPath 的函式或一些判斷式後, 整個 XmlDocument 的查詢效能會變慢. (參考以下的例子)
本文章所使用的 XML 內容 (ad.xml : 紀錄一些廣告圖與連結):
<?xml version="1.0" encoding="utf-8" ?> <ads> <item> <title>我的E政府</title> <weight>10</weight> <image>http://www.gov.tw/images/head_logo.jpg</image> <url>http://www.gov.tw/</url> </item> <item> <title>PChome Online 網路家庭</title> <weight>9</weight> <image>http://www.pchome.com.tw/img/pchomelogo.gif</image> <url>http://www.pchome.com.tw/</url> </item> <item> <title>Yahoo!奇摩</title> <weight>9</weight> <image>http://l.yimg.com/f/i/tw/hp/mh/09purple.gif</image> <url>http://tw.yahoo.com/</url> </item> <item> <title>巴哈姆特電玩資訊站</title> <weight>9</weight> <image>http://pic.bahamut.com.tw/index_w/baha_logo.jpg</image> <url>http://www.gamer.com.tw/</url> </item> </ads>假如我們現在要找出所有 <weight> 值為 9 的 <item> , 傳統上可用以下兩個方式:
(程式部分用迴圈跑 10000 次, 來區分兩種做法的效能)
- 使用 XPath 做篩選:
XmlDocument doc = new XmlDocument(); doc.Load("ad.xml"); DateTime dtStart = DateTime.Now; for (int i = 0; i < 10000; i++) { XmlNodeList nodes = doc.SelectNodes("//item[weight='9']"); foreach (XmlNode node in nodes) { string s = node.OuterXml; } } Console.WriteLine(("{0} (毫秒)" + DateTime.Now - dtStart).Milliseconds); //輸出了: 337 (毫秒)
- 在程式中判斷:
XmlDocument doc = new XmlDocument(); doc.Load("ad.xml"); DateTime dtStart = DateTime.Now; for (int i = 0; i < 10000; i++) { XmlNodeList nodes = doc.SelectNodes("//item"); foreach (XmlNode node in nodes) { if (node["weight"].InnerText == "9") { string s = node.OuterXml; } } } Console.WriteLine(("{0} (毫秒)" + DateTime.Now - dtStart).Milliseconds); //輸出了: 250 (毫秒)
儘管效能上有差異, 還是可以視情況選擇 XPath 使用的方式. 例如僅單純地使用樹狀結構查詢所需的節點, 並避免在 XPath 中使用一些函式去做文字或屬性值的判斷.
自從 .Net 出了 Linq 後, 個人感覺對 XML 的查詢更有幫助了. (效能部分因為是端看怎麼查詢, 較為主觀, 這邊就不比較效能)
以上述的例子來看, 我們可以用 Linq 的語法進行查詢: (僅供參考: 效能大約介於上述兩個做法, 300毫秒上下)
XmlDocument doc = new XmlDocument(); doc.Load("ad.xml"); var query = from n in doc.SelectNodes("//item").Cast<XmlNode>() where int.Parse(n["weight"].InnerText) == 9 select n; foreach (XmlNode node in query) { string s = node.OuterXml; }此用法的關鍵在於要將 SelectNodes 做一次 Cast , 轉為 XmlNode. 接下來用一個例子來說明 XmlDocument + Linq 的好處.
假設本文所使用的 ad.xml 檔案是一個網站用來呈現廣告的資料來源, 在前台會顯示的廣告圖共有三個, 其中權重 (<weight>) 為10的是一定要呈現的廣告, 若權重相同, 則以亂數決定.
基於少述的條件, 可以大概知道查詢上除了權重 (<weight>)外, 還要加上一個隨機的查詢條件, 以造成圖片有隨時更換的效果.
如果用過去的查詢方式, 大概會是如下的步驟:
- 先查出權重最高的並以亂數排序: 將權重為10的 <item> 再搭配上一個亂數進行排序, 然後將前三名取出.
- 如果權重為10的項目未超過三個, 需再從權重小於10的項目中再選出, 選的過程還是要加入隨機值做排序.
- 一直重覆第2步, 直到滿足廣告所需出現的數量 (ex: 三個) .
現在, 換用 Linq 做, 程式碼大致如下:
//廣告最多3則 int intAdCount = 3; //產生亂數的物件 Random rnd = new System.Random((int)DateTime.Now.Ticks); XmlDocument doc = new XmlDocument(); doc.Load("ad.xml"); var query = (from n in doc.DocumentElement.SelectNodes(".//item").Cast<XmlNode>() orderby Convert.ToInt32(n["weight"].InnerText) descending, rnd.Next(0, 10) descending select n).Take(intAdCount); foreach (XmlNode node in query) { string strTitle = node["title"].InnerText; string strImage = node["image"].InnerText; string strUrl = node["url"].InnerText; }上述的程式碼除了將 XML 中排序與亂數的問題解決, 甚至利用 Take() 就將最多只抓取三個廣告項這個條件也做掉了.
由於在 Linq 中可以用到 .Net Framework的函式庫, 所以像是日期或字串的判斷, 也就顯得更為彈性.
以上供參考, 例子舉得不好請見諒.
PS. 上述的例子, 將第7行後的程式以迴圈方式連續執行10000次, 大約花200毫秒, 效能上並不會因為用了 Linq 而不好.
沒有留言:
張貼留言