在C#中解析XML文件的最快方法?

时间:2010-06-15 15:15:07

标签: c# sql xml c#-4.0

我必须从互联网上加载许多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和insert命令将所有提取的数据从XML保存到数据库。

编辑:现在使用XmlReader,请为上面的文档提供一些XmlReader代码。现在,我已经完成了这个

void Load(string url) 
{
    _reader = XmlReader.Create(url); 
    while (_reader.Read()) 
    { 
    } 
} 

XmlReader的可用方法令人困惑。我需要的是完全击球和保龄球数据,击球和保龄球数据是不同的,而odi,t2o,ipl等在保龄球和击球中是相同的。

8 个答案:

答案 0 :(得分:9)

您可以使用XmlReader仅用于快进,快速阅读。

答案 1 :(得分:6)

抛出异常的开销可能使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中查找转换函数,并调用该函数,并将其传递给{{1}作为一个论点。

在您的情况下,这可能不是理想的方法。我真的不知道。我在这里展示它是因为有一个更大的问题在起作用。正如Steve McConnell在 Code Complete 中指出的那样,调试数据比调试代码更容易。此技术允许您将程序逻辑转换为数据。在某些情况下,使用此技术可以大大简化程序的结构。这是值得理解的。

答案 2 :(得分:2)

您可以尝试LINQ to XML。或者您可以使用this来确定要使用的内容。

答案 3 :(得分:0)

如果文档很大,那么基于流的解析器(适合您的需求)将比使用XmlDocument更快,主要是因为开销较低。查看XmlReader的文档。

答案 4 :(得分:0)

我不会说LINQ是最好的方法。我搜索了谷歌,我看到了一些HTML Agility Pack的引用。

我认为,如果你的速度有瓶颈,那将是你的下载过程。换句话说,您的性能问题似乎与XML代码无关。我认为有一些方法可以提高你的下载速度或者你的文件i / o,但我不知道它们会是什么。

答案 5 :(得分:0)

如果您知道 XML是一致且格式良好的,您可以简单地避免执行真正的XML解析并将它们作为平面文本文件处理。这是冒险的,不便携的,也很脆弱。

但它将是最快的(运行,而不是代码)解决方案。

答案 6 :(得分:0)

XmlReader是您的问题的解决方案。 XmlDocument存储了许多元信息,使得Xml易于访问,但它在内存上变得过重。我已经看到一些大小小于50 KB的Xmls转换为XmlDocument的几个MB(10或者其他)。

答案 7 :(得分:0)

如果您已将该信息转换为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自动创建的表。