Xpath синтаксис. Применение оси preceding. Применение оси namespace
XPath (XML Path Language) - язык запросов к элементам XML -документа. Разработан для организации доступа к частям документа XML в файлах трансформации XSLT и является стандартом консорциума W3C . XPath призван реализовать навигацию по DOM в XML . В XPath используется компактный синтаксис, отличный от принятого в XML. В 2007 году завершилась разработка версии 2.0, которая теперь является составной частью языка XQuery 1.0. В декабре 2009 года началась разработка версии 2.1, которая использует XQuery 1.1.
На данный момент, самой популярной версией является XPath 1.0. Это связано с отсутствием поддержки XPath 2.0 со стороны открытых библиотек. В частности, речь идёт о LibXML, от которой зависит поддержка языка в браузерах с одной стороны и поддержка со стороны серверного интерпретатора с другой.
Основы
XML имеет древовидную структуру. В документе всегда имеется корневой элемент (инструкция к дереву отношения не имеет). У элемента дерева всегда существуют потомки и предки, кроме корневого элемента, у которого предков нет, а также тупиковых элементов (листьев дерева), у которых нет потомков. Каждый элемент дерева находится на определенном уровне вложенности (далее - «уровень»). У элементов на одном уровне бывают предыдущие и следующие элементы.
Это очень похоже на организацию каталогов в файловой системе, и строки XPath, фактически, - пути к «файлам» - элементам.
Например, рассмотрим XHTML документ:
<html > <body > <div > Первый слой <span > блок текста в первом слое</ span > </ div > <div > Второй слой</ div > <div > Третий слой <span class = "text" > первый блок в третьем слое</ span > <span class = "text" > второй блок в третьем слое</ span > <span > третий блок в третьем слое</ span > </ div > <img / > </ body > </ html >
XPath-путь /html/body/*/span[@class] (полный синтаксис имеет вид /child::html/child::body/child::*/child::span ) будет соответствовать в нём двум элементам исходного документа - первый блок в третьем слое и второй блок в третьем слое .
Путь делится на шаги адресации, которые разделяются символом «косая черта» / . Каждый шаг адресации состоит из трех частей:
- ось (в данном примере child::), это обязательная часть;
- условие проверки узлов (в данном примере это имена элементов документа html, body, span, а символ * означает элемент с любым именем), также обязательная часть;
- предикат (в данном примере attribute::class), необязательная часть, заключаемая в квадратные скобки, в которой могут содержаться оси, условия проверки, функции, операторы (+, -, <, > и пр.).
Анализ ведется слева направо. Если первый символ это / , то путь адресации считается абсолютным (то есть от корня документа). При этом за узел контекста на первом шаге берется корневой элемент (html). Контекст - это некая точка отсчета, относительно которой рассчитывается следующий шаг адресации. Поэтому на каждом шаге адресации мы получаем новый набор узлов документа, и этот набор становится контекстом для следующего шага адресации.
На втором шаге адресации (child::body) контекстом становится html элемент. Ось child:: говорит о том, что необходимо найти все непосредственные потомки элемента html, а условие проверки body говорит о том, что в формируемый набор элементов нужно включить все узлы с именем body. В ходе второго шага адресации получаем набор узлов, состоящий всего из одного элемента body, который и становится элементом контекста для третьего шага.
Третий шаг адресации: child::* . Ось child:: собирает все непосредственные потомки элемента body, а условие проверки * говорит о том, что в формируемый набор нужно включить элементы основного типа с любым именем. В ходе этого шага получаем набор узлов, состоящий из трех элементов div и одного элемента img.
Четвёртый шаг адресации: child::span . Теперь контекстом является набор из четырёх элементов. И следующий набор узлов создается в четыре прохода (за четыре итерации). При первой итерации узлом контекста становится первый div. Согласно заданной оси child:: и правилу проверки span, в набор включаются непосредственные потомки div-а, имя которых равно span. При второй итерации в набор ничего добавлено не будет, так как у второго div нет потомков. Третья итерация добавит в набор сразу три элемента span, а четвёртая ничего не добавит, так как у элемента img нет потомков. Итак, в ходе проверки получен набор узлов, состоящий из четырёх элементов span. Это и будет контекстом для последующей обработки.
Следующего шага нет, поэтому будет производиться фильтрация отобранного набора. В этом и состоит отличие предикатов от шагов адресации. На каждом шаге адресации получаем новый набор, отталкиваясь от контекста, полученного на предыдущем шаге. В ходе же обработки предиката новый набор получается из текущего методом фильтрации, когда из набора исключаются узлы, не прошедшие условие проверки. В данном случае ось attribute:: говорит о необходимости проверить, если ли у узлов контекста атрибуты, а условие class требует оставить лишь те узлы, у которых задан атрибут с именем class. Фильтрация происходит за четыре итерации, но в окончательный набор попадают только два элемента span.
Оси
Оси - это база языка XPath.
- ancestor:: - Возвращает множество предков.
- ancestor-or-self:: - Возвращает множество предков и текущий элемент.
- attribute:: - Возвращает множество атрибутов текущего элемента.
- child:: - Возвращает множество потомков на один уровень ниже.
- descendant:: - Возвращает полное множество потомков.
- descendant-or-self:: - Возвращает полное множество потомков и текущий элемент.
- following:: - Возвращает необработанное множество, ниже текущего элемента.
- following-sibling:: - Возвращает множество элементов на том же уровне, следующих за текущим.
- namespace:: - Возвращает множество, имеющее пространство имён (то есть присутствует атрибут xmlns).
- parent:: - Возвращает предка на один уровень назад.
- preceding:: - Возвращает множество обработанных элементов исключая множество предков.
- preceding-sibling:: - Возвращает множество элементов на том же уровне, предшествующих текущему.
- self:: - Возвращает текущий элемент.
Существуют сокращения для некоторых осей, например:
- attribute:: - можно заменить на «@»
- child:: - часто просто опускают
- descendant:: - можно заменить на «.//»
- parent:: - можно заменить на «..»
- self:: - можно заменить на «.»
Дополнением к базе является набор функций, которые делятся на 5 групп:
Системные функции
node-set document (object, node-set?) Возвращает документ, указанный в параметре object. string format-number (number, string, string?) Форматирует число согласно образцу, указанному во втором параметре, третий параметр указывает именованный формат числа, который должен быть учтён. string generate-id (node-set?) Возвращает строку, являющуюся уникальным идентификатором. node-set key (string, object) Возвращает множество с указанным ключом (аналогично функции id для идентификаторов). string unparsed-entity-uri (string) Возвращает непроанализированный URI, если такового нет, возвращает пустую строку. boolean element-available (string) Проверяет, доступен ли элемент или множество, указанное в параметре. Параметр рассматривается как XPath. boolean function-available (string) Проверяет, доступна ли функция, указанная в параметре. Параметр рассматривается как XPath. object system-property (string) Параметры, возвращающие системные переменные, могут быть: * xsl: version - возвращает версию XSLT процессора. * xsl: vendor - возвращает производителя XSLT процессора. * xsl: vendor-url - возвращает URL, идентифицирующий производителя. Если используется неизвестный параметр, функция возвращает пустую строку. boolean lang (string) Возвращает истину, если у текущего тега имеется атрибут xml: lang, либо родитель тега имеет атрибут xml: lang и в нем указан совпадающий строке символ.Функции с множествами
- * - обозначает любое имя или набор символов, @* - любой атрибут
- $name - обращение к переменной, где name - имя переменной или параметра.
- - дополнительные условия выборки
- {} - если применяется внутри тега другого языка (например HTML), то XSLT процессор рассматривает содержимое фигурных скобок как XPath.
- / - определяет уровень дерева
Строковые функции
string string (object?) Возвращает текстовое содержимое элемента. По сути возвращает объединенное множество текстовых элементов на один уровень ниже. string concat (string, string, string*) Объединяет две или более строк number string-length (string?) Возвращает длину строки. boolean contains (string, string) Возвращает истину, если первая строка содержит вторую, иначе возвращает ложь. string substring (string, number, number?) Возвращает строку вырезанную из строки начиная с указанного номера, и если указан второй номер - количество символов. string substring-before (string, string) Если найдена вторая строка в первой, возвращает строку до первого вхождения второй строки. string substring-after (string, string) Если найдена вторая строка в первой, возвращает строку после первого вхождения второй строки. boolean starts-with (string, string) Возвращает истину если вторая строка входит в начало первой, иначе возвращает ложь. boolean ends-with (string, string) Возвращает истину если вторая строка входит в конец первой, иначе возвращает ложь. string normalize-space (string?) Убирает лишние и повторные пробелы, а также управляющие символы, заменяя их пробелами. string translate (string, string, string) Заменяет символы первой строки, которые встречаются во второй строке, на соответствующие позиции символам из второй строки символы из третьей строки. translate(«bar», «abc», «ABC») вернет BAr.Логические функции
- or - логическое «или»
- and - логическое «и»
- = - логическое «равно»
- < (<) - логическое «меньше»
- > (>) - логическое «больше»
- <= (<=) - логическое «меньше либо равно»
- >= (>=) - логическое «больше либо равно»
Числовые функции
- + - сложение
- − - вычитание
- * - умножение
- div - обычное деление (не деление нацело!)
- mod - остаток от деления
Для выбора узлов и наборов узлов в XML документе XPath использует выражения путей. Узел выбирается следуя по заданному пути или по, так называемым, шагам.
Пример XML документа
Для демонстрации синтаксиса XPath будет использоваться следующий XML документ:
Выбор узлов
Чтобы выбрать узлы в XML документе, XPath использует выражения пути. Узел выбирается следуя по заданному пути. Наиболее полезные выражения пути:
В следующей таблице приводятся некоторые выражения XPath, позволяющие сделать некоторые выборки по демонстрационному XML документу:
Выражение XPath | Результат |
---|---|
messages | Выбирает все узлы с именем "messages" |
/messages | Выбирает корневой элемент сообщений Примечание : Если путь начинается с косой черты (/), то он всегда представляет абсолютный путь к элементу! |
messages/note | Выбирает все элементы note, являющиеся потомками элемента messages |
//note | Выбирает все элементы note независимо от того, где в документе они находятся |
messages//note | Выбирает все элементы note, являющиеся потомками элемента messages независимо от того, где они находятся от элемента messages |
//@date | Выбирает все атрибуты с именем date |
Предикаты
Предикаты позволяют найти конкретный узел или узел с конкретным значением.
Предикаты всегда заключаются в квадратные скобки.
В следующей таблице приводятся некоторые выражения XPath с предикатами, позволяющие сделать выборки по демонстрационному XML документу:
Выражение XPath | Результат |
---|---|
/messages/note | Выбирает первый элемент note, который является прямым потомком элемента messages. Примечание : В IE 5,6,7,8,9 первым узлом будет , однако согласно W3C это должен быть . Чтобы решить эту проблему в IE, нужно установить опцию SelectionLanguage в значение XPath. В JavaScript: xml.setProperty("SelectionLanguage","XPath"); |
/messages/note | Выбирает последний элемент note, который является прямым потомком элемента messages. |
/messages/note | Выбирает предпоследний элемент note, который является прямым потомком элемента messages. |
/messages/note | Выбирает все элементы heading, у которых есть атрибут date |
//heading[@date="10/01/2008"] | Выбирает все элементы heading, у которых есть атрибут date со значением "10/01/2008" |
Выбор неизвестных заранее узлов
Чтобы найти неизвестные заранее узлы XML документа, XPath позволяет использовать специальные символы.
В следующей таблице приводятся некоторые выражения XPath со спецсимволами, позволяющие сделать выборки по демонстрационному XML документу:
Выбор нескольких путей
Использование оператора | в выражении XPath позволяет делать выбор по нескольким путям.
В следующей таблице приводятся некоторые выражения XPath, позволяющие сделать выборки по демонстрационному XML документу.
В данной главе на основе некоторых примеров демонстрируется базовый синтаксис XPath.
Пример XML документа
В качестве примера будет использоваться следующий XML документ:
Загрузка XML документа
Для загрузки XML документа используется объект XMLHttpRequest, который поддерживается всеми основными браузерами.
Var xmlhttp = new XMLHttpRequest();
В старых браузерах Microsoft (IE 5 и 6) используется код:
Var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
Выбор узлов
К сожалению разные браузеры поддерживают разные способы для работы с XPath.
Chrome, Firefox, Edge, Opera и Safari для выбора узлов используют метод evaluate():
XmlDoc.evaluate(xpath, xmlDoc, null, XpathResult.ANY_TYPE, null);
Internet Explorer использует метод selectNodes():
XmlDoc.selectNodes(xpath);
Выбор всех заголовков
В следующем примере выбираются все узлы heading:
/messages/note/heading
Выбор заголовка первой заметки
В следующем примере выбирается заголовок heading первого узла note элемента messages:
/messages/note/heading
Выбор всех отделов
В следующем примере выбирается текст из всех узлов desk:
/messages/note/desk
Выбираем отделы с номером больше 4
В следующем примере выбираются все узлы desk, значение которых больше 4:
/messages/note/desk
Выбираем заголовки заметок для отделов с номерами больше 4
В следующем примере выбираются все узлы heading тех узлов note, у элемент desk которых значение больше 4:
/messages/note/heading
XPath представляет язык запросов в XML. Он позволяет выбирать элементы, соответствующие определенному селектору.
Рассмотрим некоторые наиболее распространенные селекторы:
выбор текущего узла
выбор родительского узла
выбор всех дочерних узлов текущего узла
выбор всех узлов с определенным именем, в данном случае с именем "user"
выбор атрибута текущего узла, после знака @ указывается название атрибута (в данном случае "name")
выбор всех атрибутов текущего узла
выбор определенного дочернего узла по индексу, в данном случае третьего узла
выбор в документе всех узлов с именем "user"
user[@name="Bill Gates"]
выбор элементов с определенным значением атрибута. В данном случае выбираются все элементы "user" с атрибутом name="Bill Gates"
user
выбор элементов с определенным значением вложенного элемента. В данном случае выбираются все элементы "user", у которых дочерний элемент "company" имеет значение "Microsoft"
выбор в документе всех узлов с именем "company", которые находятся в элементах "user"
Действие запросов XPath основано на применении двух методов класса XmlElement :
SelectSingleNode() : выбор единственного узла из выборки. Если выборка по запросу содержит несколько узлов, то выбирается первый
SelectNodes() : выборк по запросу коллекции узлов в виде объекта XmlNodeList
Для запросов возьмем xml-документ из прошлых тем:
Теперь выберем все узлы корневого элемента, то есть все элементы user:
XmlDocument xDoc = new XmlDocument(); xDoc.Load("users.xml"); XmlElement xRoot = xDoc.DocumentElement; // выбор всех дочерних узлов XmlNodeList childnodes = xRoot.SelectNodes("*"); foreach (XmlNode n in childnodes) Console.WriteLine(n.OuterXml);
Выберем все узлы
XmlNodeList childnodes = xRoot.SelectNodes("user");
Выведем на консоль значения атрибутов name у элементов user:
XmlNodeList childnodes = xRoot.SelectNodes("user"); foreach (XmlNode n in childnodes) Console.WriteLine(n.SelectSingleNode("@name").Value);
Результатом выполнения будет следующий вывод:
Bill Gates Larry Page
Выберем узел, у которого атрибут name имеет значение "Bill Gates":
XmlNode childnode = xRoot.SelectSingleNode("user[@name="Bill Gates"]"); if (childnode != null) Console.WriteLine(childnode.OuterXml);
Выберем узел, у которого вложенный элемент "company" имеет значение "Microsoft":
XmlNode childnode = xRoot.SelectSingleNode("user"); if (childnode != null) Console.WriteLine(childnode.OuterXml);
Допустим, нам надо получить только компании. Для этого надо осуществить выборку вниз по иерархии элементов:
XmlNodeList childnodes = xRoot.SelectNodes("//user/company"); foreach (XmlNode n in childnodes) Console.WriteLine(n.InnerText);