Медленный SelectSingleNode - PullRequest
       18

Медленный SelectSingleNode

2 голосов
/ 22 декабря 2008

У меня есть простой структурированный XML-файл, подобный этому:

<ttest ID="ttest00001", NickName="map00001"/>
<ttest ID="ttest00002", NickName="map00002"/>
<ttest ID="ttest00003", NickName="map00003"/>
<ttest ID="ttest00004", NickName="map00004"/>

..... Этот XML-файл может иметь размер около 2,5 МБ.

В моем исходном коде у меня будет цикл для получения псевдонимов

В каждом цикле у меня есть что-то вроде этого:

nickNameLoopNum = MyXmlDoc.SelectSingleNode("//ttest[@ID=' + testloopNum + "']").Attributes["NickName"].Value

Эта одна строка будет стоить мне от 30 до 40 миллисекунд.

Я искал некоторые старые статьи (датированные 2002 годом), в которых говорилось, что использование какого-то скомпилированного «xpath» может помочь в ситуации, но это было 5 лет назад. Интересно, есть ли современная практика, чтобы сделать это быстрее? (Я использую .NET 3.5)

Ответы [ 4 ]

4 голосов
/ 22 декабря 2008

Использование сокращения "//" в выражении XPath приводит к большой неэффективности , поскольку вызывает поиск по всему документу XML. Использование '//' многократно увеличивает эту неэффективность.

Одним из эффективных решений проблемы является получение всех узлов атрибута "NickName" путем оценки только одного выражения XPath:

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) время.

3 голосов
/ 22 декабря 2008

Вы уже используете XPath ("// ttest ..."), и это самый медленный способ доступа к узлам документа, поскольку синтаксис "//" просматривает весь документ.

попробуйте что-то вроде ...

foreach (XMLNode node in MyXmlDoc.ChildNodes) {
    ...
}

вместо этого, xpath не требуется, и это должно быть быстрее. (неявное предположение, что это «плоский» xml-файл без вложенности. Если это так, вы скоро вернетесь, мой парень).

1 голос
/ 07 мая 2010

В ответ на Димитре

На самом деле ... выбор всего узла происходит быстрее, чем выбор только атрибутов.

У меня есть тестовый модуль, который сравнивает приведенный ниже код и (что удивительно) выбирает полный узел и обрабатывает атрибут быстрее, чем выбирает атрибуты и сразу получает значение.

поместите это в цикл 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;

            }

Противоречивый, я знаю, но .... ты должен измерить !!

0 голосов
/ 22 декабря 2008

В этом случае вы можете рассмотреть возможность считывания псевдонимов в XML-файле в массив (если ваши тестовые идентификаторы на самом деле просто последовательные целые числа) или словарь (если нет), а затем использовать его для поиска каждого псевдонима, вместо того, чтобы пытаться выполнить кучу запросов 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];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...