XPathSelectElement非常慢;是否有更好的方法来获得XPath的值?

时间:2012-04-03 21:35:32

标签: c# xml xpath linq-to-xml

我有一个基本上是XPath列表的文件,如下所示:

/Options/File[1]/Settings[1]/Type[1]
/Options/File[1]/Settings[1]/Path[1]
/Options/File[1]/Settings[2]/Type[1]
/Options/File[1]/Settings[2]/Path[1]
/Options/File[2]/Settings[1]/Type[1]
/Options/File[2]/Settings[1]/Path[1]

我需要从中等大小的XML文件(~3-5MB)中的这些XPath中指向的元素中获取值。使用XPathSelectElement运行良好,但速度极慢。是否有更快的方法来使用Linq to XML或甚至手动遍历XML?

在一个相关的问题中,XPath中的索引值和从XElement返回的元素的顺序是否相同?例如,它们会返回相同的内容:

xdoc.XPathSelectElement("/Options/File[1]/Settings[2]);

xdoc.root.Elements("File").ElementAt(0).Elements("Settings").ElementAt(1);

3 个答案:

答案 0 :(得分:2)

索引XPath(第n个孩子)通常很慢,因为需要遍历所有孩子,直到你需要的孩子。要检查 - 相对较大的文件尝试选择第一个孩子和最后一个孩子并比较差异(每个重复约1000次,并使用StopWatch进行测量)。

如果您已经显示XPath,则可以通过在迭代时缓存子节点来手动进行选择。

XML中的元素顺序很重要,因此普通的XML API将始终保持元素的顺序。请注意,属性的顺序对于XML来说并不重要,因此不同API之间的属性顺序可能不同(不太可能,但理论上可行)。

答案 1 :(得分:2)

我遇到了类似的问题:我使用一堆索引的XPath表达式在中等大小的xml文件(3 MB)中选择一些节点时表现糟糕。

但与您的解决方案相反,我在XPath表达式的每个部分都没有索引。所以我尝试使用XPath(XElement.XPathSelectElement)抛弃LINQ to XML,而是通过创建XPathNavigator并调用XPathDocument来使用CreateNavigator()。在导航器上我使用了SelectSingleNode

使用XElement.XPathSelectElement我花了137.3秒来完成所有选择(顺便说一下,程序的其余部分只使用了大约3秒)。

使用XPathNavigator.SelectSingleNode选择现在需要1.2秒整数...这是一个差不多115的因素

因此,如果任何人需要更快的XPath查询并且不想自己解析查询:如果可能的话,不要使用LINQ to XML,它似乎在性能方面表现得非常糟糕。

答案 2 :(得分:1)

我认为这就是我要去的地方。我确信可以有更多的性能改进,例如阿列克谢的建议,但在我的有限测试中,这已经至少快了10倍。

    private XElement GetElementFromXPath(XDocument xDoc, string xPath)
    {
        string[] nodes = xPath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
        XContainer xe = xDoc.Root;
        for (int i = 1; i < nodes.Length; i++)
        {
            string[] chunks = nodes[i].Split(new char[] { '[', ']' });

            int index = 0;
            if (Int32.TryParse(chunks[1], out index))
                xe = xe.Elements(chunks[0]).ElementAt(index - 1);
        }
        return (XElement)xe;
    }

这假设除了根之外的所有元素都与XPath中的索引号一起列出(对于我的场景也是如此)。