26 декабря 2009

Fast iterating over xml nodes by MSXML

Волею судеб мне приходится иметь дело с MSXML. Не самая лучшая библиотека для работы с xml, но так вот получилось. И будучи любителем конструкции for_each, обход дочерних нод в моем коде выглядит так:
BOOST_FOREACH(IXMLDOMNodePtr Node, ChildNodes(RootNode))
DoSomething(Node);
ChildNodes отдает пару итераторов, которые дают доступ по IXMLDOMNodeList через IXMLDOMNodeList::item(). Так вот на обработке большого xml файла заметил, что такая конструкция очень жутко тормозит. Я было хотел списать все это со словами "ну этож MSXML тормозная - ничего не поделаешь...". Но как оказалось, правдива только первая часть этой фразы, а "поделать" все-таки что-то можно: если перебирать ноды через IXMLDOMNodeList::nextNode(), то вместо исходных 450мс получалось обойти ноды всего за 28:
IXMLDOMNodeListPtr NodeList = RootNode->childNodes;
for (IXMLDOMNodePtr Node; Node = NodeList->nextNode(); )
DoSomething(Node);

Видимо про такие случаи Спольски рассказывал байку про маляра, который красил забор. За первый день он покрасил 20 метров забора, за второй - 10, за третий - всего 2 (цифры привожу по памяти, память дырявая). Когда его спросили, почему он так стал тормозить, ответ был великолепен: "Насяльника, так за краской все дальше и дальше ходить!" Видимо IXMLDOMNodeList::item() работает таким же образом, как и тот маляр, так что beware!

02 декабря 2009

Calculating XPath expressions by MSXML

Если посчитать выражение XPath, что может получиться?
Согласно спецификации XPath:
- список узлов
- строка
- логическое значение
- число

В .NET-е можно получить все из вышеперечисленного. А вот MSXML предлагает API только для получения списка узлов - selectNodes.

А что, если нужно получать значения других типов? Товарищи в форумах предлагают хитрый трюк - обернуть XPath выражение в XSLT, наложить его на нужный узел или документ, а в результате получить строку. Даже закрывая глаза на производительность такого трюка, таким способом не удается получить всю информацию - а именно информацию о типе результата. Ведь на выходе - всегда строка.

Если выражение - "наше", то тип результата сразу знаем. Но если "чужое", и нам нужно узнать тип результата, то в таком случае мы в тупике - из полученной строки не получится извлечь тип. Вот, 123 - это строка "123" или число 123?

Я узнал о следующем - в MSXML можно использовать JavaScript внутри XSLT. Тогда все становится достаточно просто:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:js="urn:custom-javascript"
exclude-result-prefixes="msxsl js" >
<xsl:template match="/">
<xsl:variable name="value" select="выражение"/>
<result>
<value>
<xsl:value-of select="$value"/>
</value>
<type>
<xsl:value-of select="js:TypeOf($value)"/>
</type>
</result>
</xsl:template>
<msxsl:script language="JavaScript" implements-prefix="js">
function TypeOf(strText) { return typeof strText; }
</msxsl:script>
</xsl:stylesheet>
Специальная JavaScript-функция вернет нам тип выражения, остается только не думать какая у всего этого производительность ;)