您的位置:首页 > 编程语言 > C#

[C#] XmlDocument 搭配 Linq 與 XPath

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

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

view source

print?

01
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02
<
ads
>
03
<
item
>
04
<
title
>我的E政府</
title
>
05
<
weight
>10</
weight
>
06
<
image
>http://www.gov.tw/images/head_logo.jpg%3C/
image
>
07
<
url
>http://www.gov.tw/%3C/
url
>
08
</
item
>
09
<
item
>
10
<
title
>PChome Online 網路家庭</
title
>
11
<
weight
>9</
weight
>
12
<
image
>http://www.pchome.com.tw/img/pchomelogo.gif%3C/
image
>
13
<
url
>http://www.pchome.com.tw/%3C/
url
>
14
</
item
>
15
<
item
>
16
<
title
>Yahoo!奇摩</
title
>
17
<
weight
>9</
weight
>
18
<
image
>http://l.yimg.com/f/i/tw/hp/mh/09purple.gif%3C/
image
>
19
<
url
>http://tw.yahoo.com/%3C/
url
>
20
</
item
>
21
<
item
>
22
<
title
>巴哈姆特電玩資訊站</
title
>
23
<
weight
>9</
weight
>
24
<
image
>http://pic.bahamut.com.tw/index_w/baha_logo.jpg%3C/
image
>
25
<
url
>http://www.gamer.com.tw/%3C/
url
>
26
</
item
>
27
</
ads
>
假如我們現在要找出所有 <weight>值為 9 的 <item>,傳統上可用以下兩個方式:
(程式部分用迴圈跑 10000 次,來區分兩種做法的效能)

使用 XPath 做篩選:

view source

print?

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 (毫秒)
在程式中判斷:

view source

print?

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毫秒上下)

view source

print?

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>)外,還要加上一個隨機的查詢條件,以造成圖片有隨時更換的效果。
如果用過去的查詢方式,大概會是如下的步驟:

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

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

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

view source

print?

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 而不好。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: