[英]Fastest way to parse XML files in C#?
我必須從Internet加載許多XML文件。 但是為了以更好的速度進行測試,我下載了以下格式的所有內容(超過500個文件)。
<player-profile>
<personal-information>
<id>36</id>
<fullname>Adam Gilchrist</fullname>
<majorteam>Australia</majorteam>
<nickname>Gilchrist</nickname>
<shortName>A Gilchrist</shortName>
<dateofbirth>Nov 14, 1971</dateofbirth>
<battingstyle>Left-hand bat</battingstyle>
<bowlingstyle>Right-arm offbreak</bowlingstyle>
<role>Wicket-Keeper</role>
<teams-played-for>Western Australia, New South Wales, ICC World XI, Deccan Chargers, Australia</teams-played-for>
<iplteam>Deccan Chargers</iplteam>
</personal-information>
<batting-statistics>
<odi-stats>
<matchtype>ODI</matchtype>
<matches>287</matches>
<innings>279</innings>
<notouts>11</notouts>
<runsscored>9619</runsscored>
<highestscore>172</highestscore>
<ballstaken>9922</ballstaken>
<sixes>149</sixes>
<fours>1000+</fours>
<ducks>0</ducks>
<fifties>55</fifties>
<catches>417</catches>
<stumpings>55</stumpings>
<hundreds>16</hundreds>
<strikerate>96.95</strikerate>
<average>35.89</average>
</odi-stats>
<test-stats>
.
.
.
</test-stats>
<t20-stats>
.
.
.
</t20-stats>
<ipl-stats>
.
.
.
</ipl-stats>
</batting-statistics>
<bowling-statistics>
<odi-stats>
<matchtype>ODI</matchtype>
<matches>378</matches>
<ballsbowled>58</ballsbowled>
<runsgiven>64</runsgiven>
<wickets>3</wickets>
<fourwicket>0</fourwicket>
<fivewicket>0</fivewicket>
<strikerate>19.33</strikerate>
<economyrate>6.62</economyrate>
<average>21.33</average>
</odi-stats>
<test-stats>
.
.
.
</test-stats>
<t20-stats>
.
.
.
</t20-stats>
<ipl-stats>
.
.
.
</ipl-stats>
</bowling-statistics>
</player-profile>
我在用
XmlNodeList list = _document.SelectNodes("/player-profile/batting-statistics/odi-stats");
然后使用foreach
循環此列表
foreach (XmlNode stats in list)
{
_btMatchType = GetInnerString(stats, "matchtype"); //it returns null string if node not availible
.
.
.
.
_btAvg = Convert.ToDouble(stats["average"].InnerText);
}
即使我正在離線加載所有文件,解析也很慢有沒有更快的解析方法? 或者它是SQL的問題? 我正在使用帶有插入命令的DataSet,TableAdapters將所有提取的數據從XML保存到數據庫。
編輯:現在使用XmlReader請為上面的文檔提供一些XmlReader代碼。 現在,我做到了這一點
void Load(string url)
{
_reader = XmlReader.Create(url);
while (_reader.Read())
{
}
}
XmlReader的可用方法令人困惑。 我需要的是完全擊球和保齡球數據,擊球和保齡球統計數據是不同的,而odi,t2o,ipl等在保齡球和擊球中是相同的。
您可以使用XmlReader僅用於快進,快速閱讀。
拋出異常的開銷可能使XML解析的開銷相形見絀。 您需要重寫代碼,以便它不會拋出異常。
一種方法是在詢問元素之前檢查元素是否存在。 這將有效,但它是很多代碼。 另一種方法是使用地圖:
Dictionary<string, string> map = new Dictionary<string, string>
{
{ "matchtype", null },
{ "matches", null },
{ "ballsbowled", null }
};
foreach (XmlElement elm in stats.SelectNodes("*"))
{
if (map.ContainsKey(elm.Name))
{
map[elm.Name] = elm.InnerText;
}
}
此代碼將處理您關注其名稱的所有元素,並忽略您不關注的元素。 如果map中的值為null,則表示具有該名稱的元素不存在(或沒有文本)。
事實上,如果你將數據放入一個DataTable
,並在列名DataTable
是一樣的XML元素的名稱,你甚至都不需要建立一個地圖,因為DataTable.Columns
屬性你需要的所有地圖。 此外,由於DataColumn
知道它包含哪種數據類型,因此您無需在代碼中復制該知識:
foreach (XmlElement elm in stats.SelectNodes("*"))
{
if (myTable.Columns.Contains(elm.Name))
{
DataColumn c = myTable.Columns[elm.Name];
if (c.DataType == typeof(string))
{
myRow[elm.Name] = elm.InnerText;
continue;
}
if (c.DataType == typeof(double))
{
myRow[elm.Name] = Convert.ToDouble(elm.InnerText);
continue;
}
throw new InvalidOperationException("I didn't implement conversion logic for " + c.DataType.ToString() + ".");
}
}
請注意我沒有聲明任何變量來存儲這些信息,所以我沒有機會搞砸並聲明一個與它存儲的列不同的數據類型的變量,或者在我的表中創建一個列並忘記實現填充它的邏輯。
編輯
好的,這里的東西有點棘手。 這是Python中非常常見的技術; 在C#中我認為大多數人仍然認為它有些奇怪。
如果你看看我給出的第二個例子,你可以看到它正在使用DataColumn
的元信息來確定用於將元素的值從文本轉換為其基類型的邏輯。 您可以通過構建自己的地圖來完成同樣的事情,例如:
Dictionary<string, Type> typeMap = new Dictionary<string, Type>
{
{ "matchtype", typeof(string) },
{ "matches", typeof(int) },
{ "ballsbowled", typeof(int) }
}
然后做我在第二個例子中展示的相同的東西:
if (typeMap[elm.Name] == typeof(int))
{
result[elm.Name] = Convert.ToInt32(elm.Text);
continue;
}
你的結果不再是Dictionary<string, string>
,因為現在它們可以包含不是字符串的東西; 它們必須是Dictionary<string, object>
。
但這種邏輯看起來有點笨拙; 你正在多次測試每個項目,有continue
聲明要突破它 - 它並不可怕,但它可能更簡潔。 怎么樣? 通過使用另一個映射,將類型映射到轉換函數:
Dictionary<Type, Func<string, object>> conversionMap =
new Dictionary<Type, Func<string, object>>
{
{ typeof(string), (x => x) },
{ typeof(int), (x => Convert.ToInt32(x)) },
{ typeof(double), (x => Convert.ToDouble(x)) },
{ typeof(DateTime), (x => Convert.ToDateTime(x) }
};
如果你不習慣lambda表達式,這有點難以閱讀。 類型Func<string, object>
指定一個函數,該函數將string
作為其參數並返回一個對象。 這就是該映射中的值是什么:它們是lambda表達式,也就是說函數。 它們采用字符串參數( x
),然后返回一個對象。 (我們怎么知道x
是一個字符串? Func<string, object>
告訴我們。)
這意味着轉換元素可以占用一行代碼:
result[elm.Name] = conversionMap[typeMap[elm.Name]](elm.Text);
從內去外表達:這個查找該元件的類型在typeMap
,然后查找在轉換函數conversionMap
,並調用該函數,傳遞給它elm.Text
作為參數。
在您的情況下,這可能不是理想的方法。 我真的不知道。 我在這里展示它是因為有一個更大的問題在起作用。 正如Steve McConnell在Code Complete中指出的那樣,調試數據比調試代碼更容易。 此技術允許您將程序邏輯轉換為數據。 在某些情況下,使用此技術可以大大簡化程序的結構。 值得了解。
您可以嘗試使用LINQ to XML 。 或者你可以用它來弄清楚要使用什么。
如果文檔很大,那么基於流的解析器(適合您的需求)將比使用XmlDocument更快,主要是因為開銷較低。 查看XmlReader的文檔。
我不會說LINQ是最好的方法。 我搜索了谷歌,我看到了一些HTML Agility Pack的引用。
我認為如果你有速度瓶頸,那將是你的下載過程。 換句話說,您的性能問題似乎與XML代碼無關。 我認為有一些方法可以提高你的下載速度或你的文件i / o,但我不知道它們會是什么。
如果您知道 XML是一致且格式良好的,那么您可以簡單地避免執行真正的XML解析並將它們作為平面文本文件處理。 這是危險的,不便攜的,易碎的。
但它將是最快(運行,而不是代碼)的解決方案。
XmlReader是您的問題的解決方案。 XmlDocument存儲了許多元信息,使得Xml易於訪問,但它在內存上變得過重。 我已經看到一些大小小於50 KB的Xmls轉換為XmlDocument的幾個MB(10或者其他)。
如果您已經將該信息轉換為DataSet以將其插入表中,只需使用DataSet.ReadXML() - 並使用它從數據創建的默認表。
這個玩具應用程序就是這樣做的,它可以使用您在上面定義的格式。
項目文件: http : //www.dot-dash-dot.com/files/wtfxml.zip安裝程序: http : //www.dot-dash-dot.com/files/WTFXMLSetup_1_8_0.msi
它允許您使用樹和網格格式瀏覽編輯XML文件 - 網格中列出的表是在ReadXML()之后由DataSet自動創建的表。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.