慢SelectSingleNode

时间:2008-12-22 00:53:44

标签: .net xml performance .net-3.5

我有一个简单的结构化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)

4 个答案:

答案 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];