我需要计算直接位于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身体为Stream
。
您可以使用linq到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
确实可以忽略不计,除非您做了很多迭代
您不太可能比:
更有效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
,则会逐渐递增其tally;也就是说,直接在根节点下方。
当然,它会比构建XDocument
或XmlDocument
的任何内容都更快,因为它不会花费时间和内存这样做,尽管如果您要使用XDocument
或XmlDocument
作为其他东西,那么这些方法会更快地(对他们来说,计数位很快,并且已经花费了花费的时间)。
如果您要阅读几个这样的文档,并且它们具有很多XML名称(元素和属性名称,名称名称和名称空间前缀),那么您将做得很好,以保持您传递的NameTable
对象的缓存进入settings.NameTable
属性。Nametables不是线程安全的,因此您不能仅使用相同的方法,但是当"学习"新名称并重复使用它们时,它们最昂贵,从而可以提高性能。但这是True 仅如果每个文档中都有很多名称;如果文档非常不同,则它们不会从"先验知识"中受益,而您只是在浪费周期,而不是垃圾收集每个新的XmlReader
给出的默认一个。(实际上,您的查找非常慢)。
如果您真的想要绝对最有效的能力,那么您可以通过阅读流并跟踪<...>
,</...>
和<.../>
来击败上述,但是您还必须处理一堆一堆特殊情况,因此您对上述收益不足以使努力值得。
用您的示例进行10000迭代的粗略数字:
XmlDocument: 2387373
XDocument: 1942206
XmlReader: 1872387
XmlReader with reused NameTable: 1864708
根据您的示例,使用136KIB文件的100次迭代的粗略数字:
XmlDocument: 1887930
XDocument: 1297059
XmlReader: 996636
XmlReader with reused NameTable: 961763
易于轻松:
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());
两者都将OUPUT 4
。