LINQ To XML 效能問題?
最近在 PTT 上面,有篇文章提到讀取 XML 的問題,隨手寫了 LINQ To XML 的語法來解決了。不過,似乎有網友質疑 LINQ To XML 效能不是很好?
這個 XML 檔案的內容如下,主要的撰寫的功能是比對 test 標籤,過濾出符合 mid 與 enble 特定條件的元素,並秀出子元素 modle 元素值。
例如,mid = "1" 且 enble = "TRUE" 的 modle 元素值是 "tttt"。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <root>
3: <test mid="1" enble="TRUE">
4: <modle>tttt</modle>
5: </test>
6: <test mid="2" enble="TRUE">
7: <modle>yyyy</modle>
8: </test>
9: </root>
首先,我是利用 LINQ To XML 的方式來解決,程式碼如下:
1: XDocument xd = XDocument.Load(@"XMLFile.xml");
2: var q = from x in xd.Descendants("test")
3: where (int) x.Attribute("mid") == 1 && (bool) x.Attribute("enble")
4: select x.Element("modle").Value;
5:
6: foreach (string o in q)
7: {
8: Console.WriteLine(o);
9: }
後來,網友提出 XmlDocument + XPath 的方式來解決,程式碼如下:
1: XmlDocument xmlDoc = new XmlDocument();
2: xmlDoc.Load(@"XMLFile.xml");
3:
4: XmlNodeList nodes = xmlDoc.SelectNodes("//root/test[@mid=1 and @enble='TRUE']/modle");
5: foreach (XmlElement element in nodes)
6: {
7: Console.WriteLine(element.InnerText);
8: }
有網友說:比起直接下 XPath,LINQ 那段 code 似乎造成了額外的成本。
上面兩段程式碼的比較的立足點,似乎是不太相同的。如果,LINQ To XML + XPath 的方式來比較,可能比較適當的吧?如下:
1: XDocument xd = XDocument.Load(@"XMLFile.xml");
2: var q = from x in xd.XPathSelectElements("//root/test[@mid=1 and @enble='TRUE']/modle")
3: select x.Value;
4:
5: foreach (string o in q)
6: {
7: Console.WriteLine(o);
8: }
而原先第一段 LINQ To XML 程式碼,我認為就如同使用 XmlDocument + foreach ChildNodes 的方式,如下:
1: XmlDocument xd = new XmlDocument();
2: xd.Load(@"XMLFile.xml");
3:
4: foreach (XmlNode node in xd.DocumentElement.ChildNodes)
5: {
6: if (node.Name == "test" && node.Attributes["mid"].Value == "1" &&
7: node.Attributes["enble"].Value == "TRUE")
8: {
9: Console.WriteLine(node.ChildNodes[0].InnerText);
10: }
11: }
有興趣的人可以利用以下程式碼,使用 Stopwatch 來玩玩吧,我也不知道到底哪一種是比較好的方法呢!
1: private void button1_Click(object sender, EventArgs e)
2: {
3: const int MAX = 1000000;
4: int num = 0;
5: Stopwatch sw = new Stopwatch();
6: long elapsedMilliseconds = 0;
7:
8: #region (1) LINQ To XML
9:
10: sw = Stopwatch.StartNew();
11: for (int i = 1; i <= MAX; i++)
12: {
13: XDocument xd = XDocument.Load(@"XMLFile.xml");
14: var q = from x in xd.Descendants("test")
15: where (int) x.Attribute("mid") == 1 && (bool) x.Attribute("enble")
16: select x.Element("modle").Value;
17:
18: foreach (string o in q)
19: {
20: Console.WriteLine(o);
21: }
22: }
23: sw.Stop();
24: elapsedMilliseconds = sw.ElapsedMilliseconds;
25: MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
26:
27: #endregion
28:
29: #region (2) XmlDocument + XPath
30:
31: sw = Stopwatch.StartNew();
32: for (int i = 1; i <= MAX; i++)
33: {
34: XmlDocument xmlDoc = new XmlDocument();
35: xmlDoc.Load(@"XMLFile.xml");
36:
37: XmlNodeList nodes = xmlDoc.SelectNodes("//root/test[@mid=1 and @enble='TRUE']/modle");
38: foreach (XmlElement element in nodes)
39: {
40: Console.WriteLine(element.InnerText);
41: }
42: }
43: sw.Stop();
44: elapsedMilliseconds = sw.ElapsedMilliseconds;
45: MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
46:
47: #endregion
48:
49: #region (3) LINQ To XML + XPath
50:
51: sw = Stopwatch.StartNew();
52: for (int i = 1; i <= MAX; i++)
53: {
54: XDocument xd = XDocument.Load(@"XMLFile.xml");
55: var q = from x in xd.XPathSelectElements("//root/test[@mid=1 and @enble='TRUE']/modle")
56: select x.Value;
57:
58: foreach (string o in q)
59: {
60: Console.WriteLine(o);
61: }
62: }
63: sw.Stop();
64: elapsedMilliseconds = sw.ElapsedMilliseconds;
65: MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
66:
67: #endregion
68:
69: #region (4) XmlDocument + foreach ChildNodes
70:
71: sw = Stopwatch.StartNew();
72: for (int i = 1; i <= MAX; i++)
73: {
74: XmlDocument xd = new XmlDocument();
75: xd.Load(@"XMLFile.xml");
76:
77: foreach (XmlNode node in xd.DocumentElement.ChildNodes)
78: {
79: if (node.Name == "test" && node.Attributes["mid"].Value == "1" &&
80: node.Attributes["enble"].Value == "TRUE")
81: {
82: Console.WriteLine(node.ChildNodes[0].InnerText);
83: }
84: }
85: }
86: sw.Stop();
87: elapsedMilliseconds = sw.ElapsedMilliseconds;
88: MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
89:
90: #endregion
91: }
另外,還有網友提出使用 XmlSerialize、DataSet.ReadXML 等方式,有興趣的人也可以玩玩看,順便分享一下吧。