26 марта 2018 г.

Функции current() if-else() и string.Format() XPath выражений для XPathNavigator (XPathDocument).

Скачать проект с примерами ниже можно по ссылке:

https://bitbucket.org/sergey_vaulin/xpathfunctions/src


Предыстория.


На работе появилась потребность реализовать механизм, который бы давал возможность из XML извлекать нужные данные и на их основе создавать бизнес объекты, а затем передавать их на обработку в другую систему. При этом поля требовалось забирать не просто в рамках одного узла, а сам XML представлял из себя дерево, а значит каждый узел имел ссылку на родителя, и надо было ездить по всему дерево обрабатывая узлы в зависимости от их типа. Решение было использовать XPath выражения, но оказалось не все так просто.

Подводные камни.


Для комфортного извлечения данных из XML нам требовалась поддержка XPath 2.0 (https://www.w3.org/TR/xquery-operators/) выражений и функций, которая, судя по msdn, должна быть в XPathNavigator. Но на практике оказалось, что большая часть функций попросту отсутствует и доступны только методы XPath 1.0.

Недостатки XPathNavigator.


  • Список функций представлен в internal class XPathParser который находится в System.Xml.dll (так же стоит отметить, что поддерживаются функция node(), text(), processing-instruction(), comment())

  • Отсутствует возможность передавать в функцию параметром результат запроса, только если запрос что то вернул. Пример:

    /xmlroot/node/SomeFunction()


    Ожидается, что SomeFunction будет вызвана, если /xmlroot/node что то вернёт. В XPath 2.0 данная конструкция допустима, но в .NET Framework не поддерживается. Придётся писать:

    SomeFunction(/xmlroot/node/)


    И внутри функции обрабатывать ситуацию, когда выборка пустая.
  • Есть Nuget пакет XPath2, который добавляет часть функций XPath 2.0, но не все. Список функций можно найти тут FunctionTable.cs. Сделано через добавление метода расширения для XPathNavigator, поэтому код менять практически не понадобится.

Добавление своих функций.


К счастью, есть возможность добавить свои функции, а затем использовать их в своих XPath выражениях. Пример создания есть на msdn на английском и русском языках. Поэтому тут я приведу лишь примеры тех функций, которых мне очень не хватает при работе с XPath. Делаются они через наследование от класса XsltContext своего контекста, а далее в методе IXsltContextFunction ResolveFunction производится возврат обработчика вызова функции, реализуемого интерфейс IXsltContextFunction.

Функция if-else.


Самая простая в написании функция это if-else. Её конструкция представлена ниже:

if-else(Condition, TrueResult, FalseResult)


Реализацию можно найти в файле IfElseExample.cs. Результат её исполнения:


Функция format.


Функция аналогична string.Format стандартной функции, поскольку concat() не хватает для создания своих строк. Синтаксис представлен ниже:

format (formatString, arg1, arg2...)


Реализацию можно найти в файле StringFormatExample.cs. Результат её исполнения:


Функция current.


Как известно, каждый XPath запрос в ходе исполнения хранит свой указатель на текущую позицию, но что если нам понадобится в два отдельных вызова произвести выборку. К примеру, в первом вызове выбрать один узел, а во втором используя результат предыдущего запроса произвести другой запрос. С этим нам поможет функция current().

Реализацию можно посмотреть в файле CurrentExample.cs. Результат исполнения:


Как видно на картинке, сначала мы делаем запрос к автору, после чего мы можем продолжить работу с полным XML, а по необходимости обращаться к current() узлу выбранному ранее.

Комментариев нет:

Отправить комментарий