我有一个简单的结构化XML文件,如下所示:
<ttest ID="ttest00001", NickName="map00001"/>
<ttest ID="ttest00002", NickName="map00002"/>
<ttest ID="ttest00003", NickName="map00003"/>
<ttest ID="ttest00004", NickName="map00004"/>
..... 这个xml文件大概是2.5MB。
在我的源代码中,我将有一个循环来获取昵称
在每个循环中,我都有类似的东西:
nickNameLoopNum = MyXmlDoc.SelectSingleNode("//ttest[@ID=' + testloopNum + "']").Attributes["NickName"].Value
这条单线将花费我30到40毫秒。
我搜索了一些旧文章(可追溯到2002年)说,使用某种编译的“xpath”可以帮助解决这个问题,但那是5年前的事。我想知道是否有一种现代的做法让它更快? (我正在使用.NET 3.5)
答案 0 :(得分:4)
在XPath表达式中使用“//
”缩写会导致效率低下,因为它会导致搜索整个XML文档。使用“//
”会反复增加这种低效率。
问题的一个有效解决方案是通过仅评估一个XPath表达式来获取所有“NickName
”属性节点:
<强> ttest/@NickName
强>
其中上下文节点是所有“ttest
”元素的父元素。
C#代码将如下所示:
int n = 15;
XmlDocument doc = new XmlDocument();
doc.Load("MyFile.xml");
XmlNodeList nodeList;
XmlNode top = doc.DocumentElement;
nodeList =
top.SelectNodes("ttest/@NickName");
// Get the N-th NickName, can be done in a loop for
// all n in a range
string nickName = nodeList[n].Value;
这里我们假设“ttest”元素是xml文档顶部元素的子元素。
总结,提出了一种有效的解决方案,它只评估一次XPath表达式,并将所有结果放在一个方便的IEnumerable对象(可用作数组)中,以访问任何所需的项目。 O(c)
时间。
答案 1 :(得分:3)
您已经在使用XPath(“// ttest ...”),这是访问doc节点的最慢方式,因为“//”语法在整个doc中都是如此。
尝试类似......foreach (XMLNode node in MyXmlDoc.ChildNodes) {
...
}
相反,不需要xpath,它应该更快。 (隐含的假设是它是一个没有嵌套的'扁平'xml文件。如果是这样,你很快就会复发我的小伙子。)
答案 2 :(得分:1)
回答Dimitre
实际上......选择整个节点比仅选择属性要快。
我有一个单元测试基准测试下面的代码,并且(令人惊讶地)选择完整节点并处理属性比选择属性并立即获得值更快。
将它放在10000次迭代循环中并交换注释以测试每种方式。
//XmlNodeList nodeList = document.SelectNodes("test/@NickName");
XmlNodeList nodeList = document.SelectNodes("test");
foreach (XmlNode node in nodeList)
{
//string nickName = node.Value;
string nickName = ((XmlAttribute)node.Attributes.GetNamedItem("NickName")).Value;
}
违反直觉,我知道,但......你必须衡量!!
答案 3 :(得分:0)
在这种情况下,您可能需要考虑将XML文件中的昵称读入数组(如果您的测试ID实际上只是顺序整数)或者预先读取字典(如果没有),然后使用它来查找每个昵称,而不是尝试做一堆XPath查询。通过这种方式,您可能会在查找上获得更好的性能。
编辑:像这样的东西(伪代码)
var nicknames = new Dictionary<string, string>();
foreach (XmlNode node in MyXmlDoc.ChildNodes)
{
if (node is XmlElement)
{
nicknames.Add(node.Attributes["ID"], node.Attributes["NickName"]);
}
}
...
nickNameLoopNum = nicknames[testLoopNum];