最有效的方法是直接在根元素下读取XML节点的数量

时间:2015-05-12 16:33:52

标签: c# xml performance stream

我需要计算XML流中根元素下直接位置的节点数。我不关心任何子节点。

例如,对于以下XML,它应该返回4:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <node1>
    <subnode1_1>
      <subnode_1_1_1>
        <subnode_1_1_1_1>…</subnode_1_1_1_1>
      </subnode_1_1_1>
      <subnode_1_1_2>…</subnode_1_1_2>
    </subnode1_1>
  </node1>
  <node2 />
  <node3>
    <subnode3_1>…</subnode3_1>
    <subnode3_2>…</subnode3_2>
    <subnode3_3>…</subnode3_3>
  </node3>
  <node4>…</node4>
</root>

在C#中执行此操作的最有效(我关心执行时间)的方法是什么?假设我有和XML body Stream

3 个答案:

答案 0 :(得分:4)

您可以使用Linq To XML将其删除:

var count = XDocument.Load(stream).Root.Elements.Count();
//count = 4

就效率而言,在给出的两个答案之间,我的结果是:

var sw = Stopwatch.StartNew();

XmlDocument xml = new XmlDocument();
xml.Load(stream);
int i = xml.LastChild.ChildNodes.Count; 

sw.Stop();
//971 ticks

var sw = Stopwatch.StartNew();
var count = XDocument.Load(stream).Root.Elements().Count();
sw.Stop();
//860 ticks

确实可以忽略不计的差异,除非你做了很多次迭代

答案 1 :(得分:3)

你不可能比以下更有效率:

public static int GetImmediateChildrenCount(Stream stm)
{
  using(stm)
  {
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.CheckCharacters = false; //optomisation - best avoided.
    settings.DtdProcessing = DtdProcessing.Ignore;
    int count = 0;
    using(XmlReader rdr = XmlReader.Create(stm, settings))
      while(rdr.Read())
        if(rdr.NodeType == XmlNodeType.Element && rdr.Depth == 1)
          ++count;
    return count;
  }
}

没有实际编写专门的解析器来做到这一点。

上面扫描了XmlReader忽略了除了起始,结束和空元素标签的深度以外的所有内容,并且如果深度为1,则递增其计数;也就是说,直接在根节点下面。

它肯定比构建XDocumentXmlDocument的任何东西都快,因为它不会花费时间和内存这样做,但是如果你要使用XDocument或者XmlDocument用于其他东西,那么这些方法会更快(计数位对他们来说很快,构建对象所花费的时间已经花掉了。)

如果您要阅读几个这样的文档,并且它们有许多共同的xml名称(元素和属性名称,命名空间名称和名称空间前缀),那么您最好保留NameTable的缓存传递到settings.NameTable属性的对象。 NameTables不是线程安全的,所以你不能只使用同一个,但是在“学习”新名称时它们是最昂贵的,并且重用它们可以提高后续的性能。但如果每个文件中有很多相同的名称,则是真的;如果文档非常不同,它们不会从“先验知识”中受益,而您只是浪费周期移动它们而不是垃圾收集每个新XmlReader给出的默认值。 (事实上​​,你的查找速度稍微慢一些。)

如果您确实希望绝对最有效率,那么您可以通过阅读流并跟踪<...></...>和{{1但是你还必须处理一堆特殊情况,所以你对上述内容的收益不足以让你付出相应的努力。

使用您的示例进行10000次迭代的粗略数字:

<.../>

基于您的示例,使用136KiB文件进行100次迭代的粗略数字:

XmlDocument:                     2387373
XDocument:                       1942206
XmlReader:                       1872387
XmlReader with reused NameTable: 1864708

答案 2 :(得分:2)

这很简单:

XmlDocument xml = new XmlDocument();
xml.Load(/*path to your file*/);
int i = xml.LastChild.ChildNodes.Count; //as the xml header is first child
Console.WriteLine(i.ToString());

或者@Jonesy说:

int i = XDocument.Load(/*your stream*/).Root.Elements.Count();
Console.WriteLine(i.ToString());

两者都将输出4