Xpath выбор по значению атрибутов. Примеры xpath-запросов к html

Xpath - это язык запросов к элементам xml или xhtml документа. Также как SQL, xpath является декларативным языком запросов. Чтобы получить интересующие данные, необходимо всего лишь создать запрос, описывающий эти данные. Всю «черную» работу за вас выполнит интерпретатор языка xpath.
Очень удобно, не правда ли? Давайте посмотри какие возможности предлагает xpath для доступа к узлам веб-страниц.

Создание запроса к узлам веб-страниц

Предлагаю вашему вниманию небольшую лабораторную работу, в ходе которой я продемонстрирую создание xpath запросов к веб-странице. Вы сможете повторить приведенные мной запросы и, самое главное, попробуете выполнить свои. Я надеюсь, что благодаря этому статья будет одинаково интересна новичкам и программистам знакомым с xpath по xml.

Для лабораторной нам понадобятся:
- веб-страница xhtml;
- браузер Mozilla Firefox с дополнениями;
- firebug ;
- firePath ;
(вы можете использовать любой другой браузер с визуальной поддержкой xpath)
- немного времени.

В качестве веб-страницы для проведения эксперимента предлагаю главную страницу сайта консорциума всемирной паутины ("http://w3.org "). Именно эта организация разрабатывает языки xquery(xpath), спецификацию xhtml и многие другие стандарты интернета.

Задача
Получить из xhtml-кода главной страницы w3.org информацию о конференциях консорциума при помощи запросов xpath.
Приступим к написанию xpath запросов.
Первый Xpath запрос
Открываем закладку Firepath в FireBug, выделяем с селектором элемент для анализа, нажимаем: Firepath создал xpath запрос к выбранному элементу.

Если вы выделили заголовок первого события, то запрос будет таким:

После удаления лишних индексов запрос станет соответствовать всем элементам типа «заголовок».

Firepath подсвечивает элементы, которые соответствуют запросу. Вы можете в реальном времени увидеть, какие узлы документа соответствуют запросу.

Запрос для получения информации о местах проведения конференций:
.//*[@id="w3c_home_upcoming_events"]/ul/li/div/p

Так мы получим список спонсоров:
.//*[@id="w3c_home_upcoming_events"]/ul/li/div/p

Синтаксис xpath

Давайте вернемся к созданным запросам и разберемся в том, как они устроены.
Рассмотрим подробно первый запрос

В этом запросе я выделил три части для демонстрации возможностей xpath. (Деление на части уловное)

Первая часть
.// - рекурсивный спуск на ноль или более уровней иерархии от текущего контекста. В нашем случае текущий контекст это корень документа

Вторая часть
* - любой элемент,
[@id="w3c_home_upcoming_events"] – предикат, на основе которого осуществляем поиск узла, имеющего атрибут id равным "w3c_home_upcoming_events". Идентификаторы элементов XHTML должны быть уникальны. Поэтому запрос «любой элемент с конкретным ID» должен вернуть единственный искомый нами узел.

Мы можем заменить * на точное имя узла div в этом запросе
div[@id="w3c_home_upcoming_events"]

Таким образом, мы спускаемся по дереву документа до нужного нам узла div[@id="w3c_home_upcoming_events"]. Нас абсолютно не волнует, из каких узлов состоит DOM-дерево и сколько уровней иерархии осталось выше.

Третья часть
/ul/li/div/p/a –xpath-путь до конкретного элемента. Путь состоит из шагов адресации и условия проверки узлов (ul, li и т.д.). Шаги разделяются символом " /"(косая черта).

Коллекции xpath
Не всегда удается получить доступ к интересующему узлу с помощью предиката или шагов адресации. Очень часто на одном уровне иерархии находится насколько узлов одинакового типа и необходимо выбрать «только первые» или «только вторые» узлы. Для таких случаев предусмотрены коллекции.

Коллекции xpath позволяют получить доступ к элементу по его индексу. Индексы соответствуют тому порядку, в котором элементы были представлены в исходном документе. Порядковый номер в коллекциях отсчитывается от единицы.

Исходя из того, что «место проведения» всегда второй параграф после «названия конференции», получаем следующий запрос:
.//*[@id="w3c_home_upcoming_events"]/ul/li/div/p
Где p – второй элемент в наборе для каждого узла списка /ul/li/div.

Аналогично список спонсоров мы можем получить запросом:
.//*[@id="w3c_home_upcoming_events"]/ul/li/div/p

Некоторые функции хpath
В хpath существует множество функций для работы с элементами внутри коллекции. Я приведу только некоторые из них.

last():
Возвращает последний элемент коллекции.
Запрос ul/li/div/p - возвратит последние параграфы для каждого узла списка «ul».
Функция first() не предусмотрена. Для доступа к первому элементу используйте индекс «1».

text():
Возвращает тестовое содержание элемента.
.//a – получаем все ссылки с текстом «Archive».

position() и mod:
position() - возвращает позицию элемента в множестве.
mod - остаток от деления.

Комбинацией данных функций можем получить:
- не четные элементы ul/li
- четные элементы: ul/li

Операции сравнения

  • < - логическое «меньше»
  • > - логическое «больше»
  • <= - логическое «меньше либо равно»
  • >= - логическое «больше либо равно»
ul/li , ul/li - элементы списка начиная с 3го номера и наоборот.

Самостоятельно

Попробуйте получить:
- четные URL ссылки из левого меню «Standards»;
- заголовки всех новостей, кроме первой с главной страницы w3c.org.

Xpath в PHP5

$dom = new DomDocument(); $dom->loadHTML($HTMLCode); $xpath = new DomXPath($dom); $_res = $xpath->query(".//*[@id="w3c_home_upcoming_events"]/ul/li/div/p/a"); foreach($_res => $obj) { echo "URL: ".$obj->getAttribute("href"); echo $obj->nodeValue; }

В заключение

На простом примере мы увидели возможности xpath для доступа к узлам веб-страниц.
Xpath является отраслевым стандартом для доступа к элементам xml и xhtml, xslt преобразований.
Вы можете применять его для парсинга любой html-страницы. В случае если исходный html-код содержит значительные ошибки в разметке пропустите его через

Xpath - декларативный язык запросов к элементам xml или (x)html документа и xslt преобразований.

Полезные источники :

Полная спецификация по Xpath 1.0 на русском здесь - http://citforum.ru/internet/xpath/xpath.shtml.

Xpath Online Tester - http://www.xpathtester.com/xpath/

Использование Xpath в Firebug

Для поиска DOM элементов по Xpath в Firebug есть встроенная ф-ция:

$x(xpath [, contextNode [, resultType]])

Тип результата:

XPathResult.NUMBER_TYPE XPathResult.STRING_TYPE XPathResult.BOOLEAN_TYPE XPathResult.FIRST_ORDERED_NODE_TYPE XPathResult.UNORDERED_NODE_ITERATOR_TYPE

Пример использования XPATH запросов в Firebug:

$x("//tr[@data-place]/@data-place").value $x("string(//tr[@data-place]/@data-place)")

Примеры

//div //a //*/ancestor-or-self::button

Базовый синтаксис

Пути

. # текущий контекст.// # рекурсивный спуск (на ноль или более уровней от текущего контекста) /html/body # абсолютный путь table//a # относительный путь a # путь относительно текущего контекста //* # все в текущем контексте li/*/a # ссылки, являющиеся "внуками" для li //a|//button # ссылки и кнопки (объединение двух множеств узлов)

Отношения

a/i/parent::p # непосредственный родитель,

P/ancestor::* # все родители (http://bit.ly/1ta7mh9) p/following-sibling::* # все следующие братья p/preceding-sibling::* # все предыдущие братья p/following::* # все следующие элементы кроме потомков (http://bit.ly/1ta83H5) p/preceding::* # все предыдущие элементы кроме предков p/descendant-or-self::* # контекстный узел и все его потомки p/ancestor-or-self::* # контекстный узел и все его предки

Получение узлов

/div/text() # получить текстовые узлы /div/text() # получить первый текстовый узел

Позиция элемента

a # первый элемент a # последний элемент a[i] # вторая ссылка, которая содержит элемент a # Первые 3 ссылки ul # список (UL), первый элемент которого содержит значение "OK" tr # не четные элементы tr # четные элементы p/text() # второй текстовый узел

Атрибуты и фильтры

Указывает на фильтрацию элементов

Input[@type=text] # атрибуты задаются с префиксом @ input[@class="OK"] # p # параграфы без атрибутов *[@style] # все элементы с атрибутом style a[. = "OK"] # ссылки со значением "OK" a/@id # идентификаторы ссылок a/@* # все атрибуты ссылок a[@id and @rel] # ссылки, которые содержат атрибуты id и rel a[@id][@rel] # то же самое a # ссылки содержат элемент или

Функции

Базовые функции Xpath - http://www.w3.org/TR/xpath/#corelib

Name() # возвращает имя элемента string(val) string(a/@id) # получить значение атрибута substring(val, from, to) substring(@id, 1, 6) # вырезать часть строки substring-after(val, from) substring-after(@id, "FROM") substring-before string-length() # возвращает число символов в строке count() # возвращает количество элементов concat() normalize-space() # аналог trim, удаляет пробелы Пример: starts-with() contains() translate("bar","abc","ABC") # BAr

Математика

//p[ position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5) ] //tr position() # div # деление mod # остаток от деления ceiling() # минимальное целое floor() # максимальное целое round() sum()

Группирование

(table/tbody/tr) # последняя строка из всех таблиц (//h1|//h2) # заголовок первого или второго уровня, который содержит "Text"

Составные условия фильтрации

Сегодня мы плотно рассмотрим тему использования XPath вместе с PHP. Вы увидите на примерах, как XPath значительно сокращает количество кода. Рассмотрим использование запросов и функций в XPath.

В начале, предоставлю вам два типа документов: DTD и XML, на примере которых мы рассмотрим функционирование PHP DOM XPath. Вот как они выглядят:

A Book An Author Horror chapter one Another Book Another Author Science Fiction chapter one

Основные XPath запросы

Простой синтаксис XPath позволяет обращаться к элементам XML документа. Наиболее простым способом, можно прописать путь к желаемому элементу. Используя XML документ, поданный выше, следующий XPath запрос возвратит коллекцию текущих элементов, находящихся в элементе book:

//library/book

Вот так! Два слеша впереди определяют корневой элемент документа, а один слеш производит переход к дочернему элементу book. Это просто и быстро, не так ли?

Но что, если вы хотите выбрать определенный элемент book из множества? Давайте предположим, что вам нужны книги «Определенного автора». XPath запрос для этого будет следующим:

//library/book/author/..

Вы можете использовать text() в квадратных скобках для сравнения значения узла. Также «/..» означает, что мы хотим использовать родительский элемент (т. е. возвращаемся на один узел выше).

XPath запросы осуществляются с помощью одной или двух функций: query() и evaluate() . Обе формируют запрос, но разница в возвращаемом результате. query() всегда будет возвращать DOMNodeList , в отличии evaluate() будет возвращать текстовый результат, если это возможно. Для примера, если ваш XPath запрос будет возвращать количество книг написанных определенным автором, тогда query() возвратит пустой DOMNodeList, evaluate() просто возвратит число, вы можете использовать это непосредственно для получения данных из узла.

Код и преимущества скорости XPath

Давайте рассмотрим простой пример, который будет возвращать количество книг написанных конкретным автором. Первый метод мы рассмотрим так, как мы всегда делаем, без использования XPath. Сейчас вы поймете, как это делается без XPath и насколько это проще делать с XPath.

domDocument->getElementsByTagName("author"); foreach ($elements as $element) { if ($element->nodeValue == $author) { $total++; } } return $number; }

Следующий метод возвращает такой же результат, но используется XPath для выбора тех книг, которые написанные определенным автором.

domDocument); $result = $xpath->query($query); return $result->length; }

Заметьте, нам не нужно повторно проверять значение каждого элемента, чтобы определить, каким автором написана каждая книга. Но мы можем более упростить код, используя XPath функцию count() , чтобы подсчитать содержимое элементов этого пути.

domDocument); return $xpath->evaluate($query); }

Мы можем получить информацию, которую нам нужно, с помощью одной строки XPath запроса. Нет необходимости создавать множество PHP фильтров. Это наиболее простой и быстрый способ написать этот функционал!

Заметьте, что evaluate() использовался в последнем примере. Это потому что функция count() возвращает текстовый результат. Используя query(), возвратиться DOMNodeList, но он будет пустым.

XPath стоит использовать, не только потому что это делает ваш PHP код проще, это также дает преимущество в скорости выполнения кода. Я заметил, что первая версия была на 30% быстрее в среднем, по сравнению со второй. Но третья на 10% быстрее первой. Конечно же, это зависит от вашего сервера и запросов, которые используете. Использование XPath в его чистом виде, дает величайшие результаты в скорости и простоте написания кода.

XPath Функции

Вот несколько функций, которые могут использоваться с XPath. Также вы найдете множество ресурсов, которые детально рассматривают каждую доступную функцию. Если вам нужно вычислять DOMNodeList или сравнивать nodeValue (значение узла), можно найти подходящую XPath функцию, которая исключит использование лишнего PHP кода.

Вы уже это знаете на примере count() функции. Давайте воспользуемся функцией id(), для получение названий книг с заданными ISBN. Для этого нужно использовать следующее XPath выражение:

id("isbn1234 isbn1235")/title

Заметьте, значения, которые вы ищете не стоит заключать в скобки, только разделите их пробелами. Также не вздумайте влепить запятую:

domDocument); $result = $xpath->query($query); $books = array(); foreach ($result as $node) { $book = array("title" => $booknode->nodeValue); $books = $book; } return $books; }

Обработка сложных функций в XPath невероятно проста.

Использование PHP функций совместно с XPath

Иногда вам необходимо будет больше функциональности, которую не могут предоставить стандартные функции XPath. К счастью, PHP DOM позволяет взаимодействовать собственным функциям PHP с XPath запросами.

Давайте рассмотрим пример, который возвращает количество слов в названии книги. В этой простейшей функции, мы напишем следующее:

domDocument); $result = $xpath->query($query); $title = $result->item(0)->getElementsByTagName("title") ->item(0)->nodeValue; return str_word_count($title); }

Но, мы также можем включить функцию str_word_count() непосредственно в XPath запрос. Это можно сделать с помощью нескольких шагов. Прежде всего, нам нужно зарегистрировать namespase с XPath объектом. PHP функции в XPath запросах вызываются с помощью строки «php:functionString», после чего прописывается имя желаемой функции. Также, namespace более подробно рассматривается на http://php.net/xpath. Другие значения namespace будут выдавать ошибку. После этого нам нужно вызвать registerPHPFunctions(). Эта функция сообщает PHP, что когда идет обращение через namespace «php:», этот вызов будет обрабатывать именно PHP.

Примерный синтаксис вызова функций будет следующим:

php:functionString("nameoffunction", arg, arg...)

Давайте совместим все это вместе в следующем примере функции getNumberOfWords():

domDocument); //регистрируем php namespace $xpath->registerNamespace("php", "http://php.net/xpath"); //теперь php функции могут вызываться в xpath запросах $xpath->registerPHPFunctions(); $query = "php:functionString("str_word_count",(//library/book[@isbn = "$isbn"]/title))"; return $xpath->evaluate($query); }

Заметьте, что вам не нужно вызывать XPath функцию text() чтобы получить текст узла. Метод registerPHPFunctions() делает это автоматизированным. Хотя, следующий пример строки кода также будет валидным:

php:functionString("str_word_count",(//library/book[@isbn = "$isbn"]/title))

Регистрирование PHP функций не ограничено для функций, которые включены в PHP. Вы можете определить свои собственные функции и использовать их внутри XPath. Единственное отличие в том, что придется использовать «php:function» вместо «php:functionString».

Давайте напишем функцию, которая будет за пределами класса, для демонстрации базовой функциональности. Функция, которую мы будем использовать, возвращает книги автора «George Orwell». Она должна возвращать true для каждого узла, который вы хотите включить в запрос.

nodeValue == "George Orwell"; }

Аргумент, который передается в функцию, является массивом DOM элементов. Эта функция проходит по массиву и определяет нужные элементы, после чего включает их в DOMNodeList. В этом примере, испытываемый узел был /book, также мы использовали /author для определения нужных элементов.

Теперь мы можем создать функцию getGeorgeOrwellBooks():

domDocument); $xpath->registerNamespace("php", "http://php.net/xpath"); $xpath->registerPHPFunctions(); $query = "//library/book1"; $result = $xpath->query($query); $books = array(); foreach($result as $node) { $books = $node->getElementsByTagName("title") ->item(0)->nodeValue; } return $books; }

Если функция compare() статическая, тогда вам нужно внести поправку в XPath запрос:

//library/book

Говоря по правде, вся эта функциональность могла быть реализована с помощью чистого XPath кода. Но, пример показывает, как можно расширять XPath запросы и делать их более комплексными.

В завершение

XPath - это отличный способ сократить количество кода и повысить его обработку, при работе с XML. Дополнительная функциональность PHP DOM позволяет вам расширить XPath функции. Это реально полезная штука, если вы будете ее использовать и углубляться в специфику, вам придется меньше и меньше писать кода.



(предок) содержит всех предков контекстного узла, включая родителей, дедушек, прадедушек и т.д. Эта ось всегда содержит корневой узел - если только контекстным узлом не является сам корневой узел.

Взгляните на листинг 7.3, в котором при помощи оси

осуществляется поиск имен (хранимых в элементе ) всех предков элементов . Листинг 7.3. Применение оси ancestor
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Вот результат применения этой таблицы стилей к

:

Применение оси ancestor-or-self

содержит всех предков контекстного узла, а также сам контекстный узел. Это означает, помимо прочего, что такая ось всегда содержит корневой узел.

В листинге 7.4 добавлены атрибуты

со значением «Steve» в весь документ. Листинг 7.4. planets.xml с атрибутами AUTHOR
.0553
58.65
1516
.983
43.4
.815
116.75
3716
.943
66.8
1
2107
1
128.4

Предположим теперь, что я хочу перечислить по имени всех предков элементов

, имеющих атрибут , а также текущий элемент , если у него есть атрибут . Это можно сделать при помощи оси и функции (листинг 7.5). Листинг 7.5. Применение оси ancestor-or-self
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Вот результат; показаны выбранные предки всех трех элементов

, включая сам элемент , при условии, что у него имеется атрибут :

Применение оси descendant

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

В следующем примере (листинг 7.6) демонстрируется работа с этой осью. На этот раз я хочу добавить примечание к элементу

Меркурия:
(Извините, но Меркурий взорвался и больше не доступен.). Чтобы найти Меркурий, мне достаточно только проверить, имеет ли какой-либо потомок элемента строковое значение «», что я сделаю при помощи выражения XPath внутри предиката выбора. Листинг 7.6. Применение оси descendant
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Sorry. Mercury has blown up and is no longer available.

Вот результирующий документ, дополненный новым элементом

только для Меркурия:
.0553
58.65
1516
.983
43.4
Sorry, Mercury has blown up and is no longer available.
.815
116.75
3716
.943
66.8

Применение оси descendant-or-self

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

В следующем примере (листинг 7.7) демонстрируется работа с осью. В этом случае я создал упрощенную таблицу стилей (подробнее об упрощенных таблицах стилей см. главу 2), которая обрабатывает все элементы с использованием потомков, генерируя уже знакомую нам HTML-таблицу данных о планетах.

Листинг 7.7. Применение оси descendant-or-self

Вот и все. Я применил здесь упрощенную таблицу стилей, чтобы подчеркнуть, что при помощи таких осей потомков, как

или , вы можете автоматически обрабатывать все выбираемые узлы, во многом аналогично тому, как это делают элементы или .

Применение оси following

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

В этом примере (листинг 7.8) я выбираю каждый элемент И копирую все последующие элементы в результирующий документ.

Листинг 7.8. Применение оси following
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Меркурия, он копирует все последующие элементы - то есть Венеру, затем всех потомков Венеры, далее Землю и затем всех потомков Земли. После этого он выбирает элемент Венеры и копирует все следующие элементы, то есть Землю и всех потомков Земли:
.815
116.75
3716
.943
66.8
.815
116.75
3716
.943
66.8
1
2107
1
128.4
1
2107
1
128.4
1
2107
1
128.4
1
2107
1
128.4

С другой стороны, при использовании оси

в результирующий документ будут скопированы только следующие братья, то есть только элементы , как мы увидим в следующем разделе.

Применение оси following-sibling

содержит всех последующих братьев контекстного узла.

Например, я могу выбрать каждый элемент

и скопировать в результирующий документ все узлы в оси следующим образом (листинг 7.9). Листинг 7.9. Применение оси following-sibling
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

При этом сначала копируются два узла-брата, следующие за Меркурием (Венера и Земля), затем копируется следующий узел-брат Венеры, Земля. У самой Земли нет следующих за ней братьев, поэтому результат выглядит так:

.815
116.75
3716
.943
66.8
1
2107
1
128.4
1
2107
1
128.4

Применение оси namespace

содержит узлы пространств имен контекстного узла. Заметьте, что эта ось пуста, если контекстным узлом не является элемент. У элемента присутствует узел пространства имен для:

Каждого атрибута элемента, чье имя начинается с «xmlns:»;

Каждого атрибута элемента-предка, чье имя начинается с «xmlns:» (конечно, если сам элемент или ближайший предок не объявит пространство имен заново);

Атрибута

, если элемент или предок имеет атрибут .

В следующем примере (листинг 7.10) я хочу отобразить пространство имен элемента

в результирующем документе, и в исходном документе я присвоил пространству имен значение «http://www.starpowder.com». Листинг 7.10. planets.xml с объявлением пространства имен
.0553
58.65
1516
.983
43.4

Вот таблица стилей (листинг 7.11), в которой я проверяю пространства имен, используемые в элементе

. Листинг 7.11. Применение оси namespace в planets.xml
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

А вот результирующий документ (заметьте, что вид документа может меняться в зависимости от процессора XSLT):

Применение оси parent

Ось parent (родитель) содержит родителя (и только родителя) контекстного узла, если таковой имеется.

Предположим, что я хочу изменить содержимое элемента Земли

на «>> (Масса Земли принимается за 1). В следующем шаблоне (листинг 7.12) для этого проверяется, содержит ли родитель элемента элемент со строковым значением «Earth». Листинг 7.12. Применение оси parent
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

The mass of Earth is set to 1.

И вот результат:

.0553
58.65
1516
.983
43.4
.815
116.75
3716
.943
66.8
The mass of Earth is set to 1.
2107
1
128.4

Применение оси preceding

содержит все узлы, расположенные перед контекстным узлом в соответствии с установленным в документе порядком, исключая всех предков контекстного узла, а также исключая узлы атрибутов и узлы пространств имен.

Пусть, например, мне нужно задать для содержимого элемента

текст «» (Эта планета расположена дальше от Солнца, чем Меркурий.), если рассматриваемая планета действительно дальше от Солнца, чем Меркурий. Один из способов сделать это - проверить, расположен ли Меркурий перед рассматриваемой планетой в соответствии с установленным в документе порядком, при помощи оси (листинг 7.13). Листинг 7.13. Применение оси preceding
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
This planet is farther from the Sun than Mercury.

Если текущая планета расположена после Меркурия, я могу вставить сообщение в ее элемент

. Результат следующий:
.0553
58.65
1516
.983
43.4
.815
116.75
3716
.943
This planet is farther from the Sun than Mercury.
1
2107
1
This planet is farther from the Sun than Mercury.

Применение оси preceding-sibling

содержит всех предшествующих братьев контекстного узла. Заметьте, что если контекстным узлом является узел атрибута или узел пространства имен, ось будет пуста.

Что, если, например, вам нужно создать шаблон, который будет выбирать только элементы

в элементе Меркурия? Для этого можно проверить, существуют ли братья, предшествующие элементу , которые являются элементами со строковым значением «». Если применить ось (листинг 7.14), поиск будет ограничен текущим элементом , что означает, что Меркурий не будет выбран, если вы только не находитесь в нужном элементе . Листинг 7.14. Применение оси preceding-sibling
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

А вот результат:

.0553
58.65
1516
.983
This is the planet Mercury, closest to the Sun.
.815
116.75
3716
.943
66.8
1
2107
1
128.4

Применение оси self

содержит только контекстный узел. В соответствии с одним из сокращений XPath, как мы увидим дальше, вместо «» можно использовать «.».

Эту ось полезно иметь в виду, поскольку, как вы помните из главы 4, если не задать ось, осью по умолчанию будет

, а в некоторых случаях вам может понадобиться обратиться к действующему узлу. Например, примет значение истины только если контекстным узлом будет элемент .

В следующем примере я объединяю шаблоны для элементов

и в один шаблон. Поскольку у этих элементов разный формат, я должен обращаться с ними по-разному внутри одного и того же шаблона (что можно сделать проверкой значений оси , которая возвращает непустой набор узлов, если контекстным, узлом является элемент , и , возвращающей непустой набор узлов, если контекстным узлом является элемент ):

На этом мы завершаем рассмотрение новых осей XPath. Давайте перейдем к примерам.

Примеры путей расположения

Мы изучили достаточно теории путей расположения. Но, понятно, лучше всего осваивать этот материал на примерах, поэтому я привожу следующий список примеров путей расположения (сокращенные варианты рассматриваются после этого списка):

. Возвращает дочерние элементы контекстного узла; . Возвращает все дочерние текстовые узлы контекстного узла; . Возвращает всех детей контекстного узла; . Возвращает атрибут контекстного узла; . Возвращает все элементы-потомки контекстного узла; . Возвращает всех предков контекстного узла; . Возвращает предков контекстного узла. Если контекстным узлом тоже является , возвращает также контекстный узел; . Возвращает элементы-потомки контекстного узла. Если контекстным узлом тоже является , возвращает также контекстный узел; . Возвращает контекстный узел, если им является элемент ;
child::PLANET/descendant::NAME
. Возвращает элементы-потомки дочерних элементов контекстного узла; . Возвращает всех внуков контекстного узла; . Возвращает корневой узел; . Возвращает все элементы в документе;
/descendant::PLANET/child::NAME
. Возвращает все элементы с родителем в документе; . Возвращает третьего ребенка контекстного узла;
child::PLANET
. Возвращает последнего ребенка контекстного узла;
/descendant::PLANET
. Возвращает третий элемент в документе;
child::PLANETS/child::PLANET/child::NAME
. Возвращает третий элемент четвертого элемента элемента ; . Возвращает всех детей контекстного узла после первых трех;
preceding-sibling::NAME
. Возвращает второй предыдущий элемент-брат контекстного узла;
child::*
. Возвращает детей и контекстного узла.
child::*
. Возвращает последнего ребенка или контекстного узла.

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

; возвращает детей контекстного узла, у которых есть дети с текстом, равным «Venus»; возвращает всех детей контекстного узла, у которых есть атрибут со значением «»; возвращает шестого ребенка контекстного узла, только если у этого ребенка есть атрибут со значением «days». Можно также написать ; возвращает всех детей контекстного узла, у которых есть атрибут и атрибут ;
//PLANET
" выбирает все элементы , значение которых отлично от значения любого предшествующего элемента ; выбирает любой элемент , который является первым ребенком своего родителя; выбирает первых пятерых детей контекстного узла, у которых есть атрибут .

Проверка выражений XPath

В пакет Xalan входит удобная программа-пример, ApplyXPath.java, позволяющая применить выражение XPath к документу и посмотреть на результат, что очень помогает при тестировании. Для запуска этого примера вам нужно будет скомпилировать

в при помощи утилиты java.exe, входящей в поставку Java.

В качестве примера я применю выражение XPath «

» к при помощи . Ниже показан результат, отображающий все элементы , дочерние по отношению к элементам (теги добавлены программой ApplyXPath):
%java ApplyXPath planets.xml PLANET/NAME
MercuryVenusEarth

XPath 2.0

XPath находится в стадии обновления, и в него включаются средства поддержки XSLT 2.0 (см. www.w3.org/TR/xpath20req). Задачи XPath 2.0 следующие:

Упрощение операций с содержимым типов, поддерживаемых схемой XML;

Упрощение операций со строковым содержимым;

Поддержка соответствующих стандартов XML;

Улучшение удобства использования;

Улучшение функциональной совместимости;

Улучшение поддержки международных языковых средств;

Сохранение обратной совместимости;

Повышенная эффективность процессора.

Следующий список дает обзор требований XPath. Главные пункты - поддержка схемы XML и регулярных выражений, что дает средства работы со строками и поиска в строках. (Дополнительную информацию о регулярных выражениях можно почерпнуть по адресу http://www.perldoc.com/perl5.6/pod/perlre.html.) В соответствии с W3C, XPath 2.0:

Должен поддерживать архитектуру XML W3C, хорошо взаимодействуя с другими стандартами в семействе XML;

Должен выражать свою модель данных в терминах информационного множества (infoset) XML;

Должен предоставлять общий ключевой синтаксис для XSLT 2.0 и XML Query language 1.0;

Должен поддерживать явное сравнение «

» или «» и синтаксис равенства;

Должен расширять множество функций агрегации (например, пользователи XSLT часто требовали добавить функции

и );

Должен сохранять обратную совместимость с XPath 1.0;

Должен предоставлять функции пересечения и разности то есть - XPath 1.0 поддерживает объединение двух наборов узлов, и к этому должны быть добавлены функции пересечения и разности;

Должен поддерживать операцию унарного плюса (поскольку в схеме XML у десятичных чисел может присутствовать лидирующий плюс);

Должен улучшать удобство использования;

Должен снизить ограничения на шаги расположения;

Должен реализовывать условную операцию, оперирующую тремя выражениями - выражением 1 (логическая операция), выражением 2 и выражением 3. Если выражение 1 принимает значение «истина», должно вычисляться выражение 2, а если выражение 1 принимает значение «ложь», должно вычисляться выражение 3;

Должен определять последовательный синтаксис для подвыражений, обрабатывающих коллекции элементов;

Должен поддерживать дополнительные строковые функции. Например, W3C рассматривает вопрос добавления средств для замены в строках, заполнения символами и преобразований регистра;

Должен поддерживать функции агрегации при применении к коллекциям. Например, некоторым пользователям XPath 1.0 требовалось применить такую функцию агрегации, как

, к значениям выражений, примененных к наборам узлов;

Должен поддерживать регулярные выражения для поиска в строках с использованием нотации регулярных выражений, установленной в схеме XML;

Должен поддерживать элементарные типы данных схемы XML. То есть в дополнение к типам, поддерживаемым моделью данных XPath 1.0, - строке, числу, логическому значению и набору узлов - модель данных XPath 2.0 должна поддерживать элементарные типы данных схемы XML;

Должен поддерживать представления чисел с плавающей точкой одинарной и двойной точности, поддерживаемые схемой XML, которая использует научную нотацию;

Должен определять подходящий набор функций для работы пользователя с элементарными типами данных схемы XML;

Должен добавлять в XPath тип данных «список» (поскольку схема XML позволяет определять простые типы, унаследованные от списка);

Должен поддерживать доступ к значениям простых типов элементов и атрибутов. Поскольку схемы XML представляют много новых типов, XPath 2.0 должен поддерживать доступ к собственному, простого типа, значению элемента или атрибута;

Должен определять поведение операторов для нулевых аргументов;

Должен иметь средства для выбора элементов или атрибутов на основе явного типа схемы XML;

Должен иметь средства для выбора элементов или атрибутов на основе иерархии типов схемы XML;

Должен иметь средства для выбора элементов на основе групп подстановки схемы XML;

Должен поддерживать средства поиска, основанные на уникальных ограничениях и ключах схемы.

Хотя мы подошли к концу главы, о XPath сказано еще не все. Тема будет продолжена в следующей главе, в которой мы более внимательно рассмотрим доступные в XPath функции и функции, уже встроенные в XSLT.