В этом уроке по Java XPath мы узнаем, что такое библиотека XPath, что такое типы данных XPath и научимся создавать синтаксис выражений XPath для извлечения информации из XML-файла или документа. Эта информация может быть XML-узлами или XML-атрибутами или даже комментариями.
В этом руководстве мы будем использовать этот XML для оценки различных выражений XPath.
<?xml версия="1.0" кодировка="utf-8" ?><инвентарь><!--Тест — это тестовый комментарий--><год книги="2000"><title>Снежная катастрофа</title><автор>Нил Стивенсон</автор><издатель>Spectra</издатель><isbn>0553380958</isbn><цена>14.95</цена></книга><год книги="2005"><title>Пылающая башня</title><автор>Ларри Нивен</автор><автор>Джерри Пурнелл</автор><издатель>Pocket</издатель><isbn>0743416910</isbn><цена>5.99</цена></книга><год книги="1995"><title>Зодиак</title><автор>Нил Стивенсон</автор><издатель>Spectra</издатель><isbn>0553573862</isbn><цена>7.50</цена></книга></инвентарь>
В следующей таблице приведено несколько выражений XPath для быстрого ознакомления:
Описание | Выражение XPath |
---|---|
Получите названия книг, написанных после 2001 года | //книга[@год>2001]/название/текст() |
Получите названия книг дешевле 8 долларов | //книга[цена<8]/название/текст() |
Получить название первой книги | //книга[1]/название/текст() |
Получить всех писателей | //книга/автор/текст() |
Подсчитать все названия книг | количество(//книга/название) |
Получить названия книг с именем автора, начинающимся с Нил | //книга[начинается-с(автор,'Нил')] |
Получить названия книг с именем автора, содержащим Нивен | //книга[содержит(автор,'Нивен')] |
Получите количество названий книг, написанных Нилом Стивенсоном | count(//book[author='Нил Стивенсон']) |
Получите названия книг, написанных Нилом Стивенсоном | //книга[author='Нил Стивенсон']/title/text() |
1. Что такое XPath?
XPath — это синтаксис, используемый для описания частей XML- документа. С помощью XPath мы можем ссылаться на элемент, любой атрибут элементов, все конкретные элементы, содержащие текст, и многие другие комбинации. Таблица стилей XSLT использует выражения XPath в сопоставлении и выбирает атрибуты различных элементов, чтобы указать, как следует преобразовать документ.
XPath иногда может быть полезен при тестировании веб-сервисов с использованием XML для извлечения и проверки ответов API.
XPath использует синтаксис языка, очень похожий на тот, что мы уже знаем. Синтаксис представляет собой смесь базовых выражений языка программирования(джокеры, такие как $x*6) и выражений путей Unix-подобных(таких как /inventory/author).
В дополнение к базовому синтаксису XPath предоставляет набор полезных функций(таких как count() или contains(), во многом похожих на вызовы служебных функций), которые позволяют искать различные фрагменты данных внутри документа.
2. Модель данных XPath
XPath рассматривает XML-документ как дерево узлов. Это дерево очень похоже на объектную модель документа, т. е. дерево DOM, поэтому, если вы знакомы с DOM, вы легко поймете, как строить базовые выражения XPath.
В модели данных XPath существует семь типов узлов:
- Корневой узел(только один на документ)
- Элементные узлы
- Атрибутивные узлы
- Текстовые узлы
- Узлы комментариев
- Узлы инструкций по обработке
- Узлы пространства имен
2.1 Корневой узел
Корневой узел — это узел XPath, содержащий весь документ. В нашем примере корневой узел содержит элемент <inventory>. В выражении XPath корневой узел указывается с помощью одной косой черты('/').
2.2 Узлы элементов
Каждый элемент в исходном XML-документе представлен узлом элемента XPath.
Например, в нашем примере XML ниже представлены узлы элементов.
- книга
- заголовок
- автор
- издатель
- isbn
- цена
2.3 Узлы атрибутов
Как минимум, узел элемента является родителем одного узла атрибута для каждого атрибута в исходном документе XML. Эти узлы используются для определения характеристик конкретного узла элемента.
Например, в нашем XML-фрагменте «год» является узлом атрибута.
2.4 Текстовые узлы
Текстовые узлы освежающе просты. Они содержат текст из элемента. Если исходный текст в документе XML содержал ссылки на сущности или символы, они разрешаются до создания текстового узла XPath.
Текстовый узел — это текст, чистый и простой. Текстовый узел должен содержать как можно больше текста. Помните, что следующий или предыдущий узел текстового узла не может быть другим текстовым узлом.
Например, все значения в нашем фрагменте XML являются текстовыми узлами, например «Снежная катастрофа» и «Нил Стивенсон».
2.5 Узлы комментариев
Узел комментария также очень прост — он содержит некоторый текст. Каждый комментарий в исходном документе становится узлом комментария. Текст узла комментария содержит все, что находится внутри комментария, за исключением открывающего <!— и закрывающего —>.
Например:
<!--Test is test comment-->
2.6 Узлы инструкций по обработке
Узел инструкции обработки состоит из двух частей: имени(возвращаемого функцией name()) и строкового значения. Строковое значение — это все, что находится после имени <?xml, включая пробелы, но не включая ?>, который закрывает инструкцию обработки.
Например:
<?xml version="1.0" encoding="utf-8"?>
2.7 Узлы пространства имен
Узлы пространства имен почти никогда не используются в таблицах стилей XSLT; они существуют в первую очередь для удобства процессора XSLT.
Помните, что объявление пространства имен(например, xmlns:auth=»http://www.authors.net»), даже если технически оно является атрибутом в источнике XML, становится узлом пространства имен, а не узлом атрибута.
3. Типы данных XPath
В Java выражение XPath может возвращать один из следующих типов данных:
- node-set – представляет набор узлов. Набор может быть пустым или содержать любое количество узлов.
- node(Java поддерживает его) – представляет один узел. Он может быть пустым или содержать любое количество дочерних узлов.
- boolean – представляет значение true или false. Имейте в виду, что строки true или false не имеют специального значения или значения в XPath; см. раздел 4.2.1.2 в главе 4 для более подробного обсуждения булевых значений.
- number – представляет число с плавающей точкой. Все числа в XPath и XSLT реализованы как числа с плавающей точкой; тип данных integer(или int) не существует в XPath и XSLT. В частности, все числа реализованы как числа с плавающей точкой IEEE 754, тот же стандарт, который используется примитивными типами Java float и double. Помимо обычных чисел, существует пять специальных значений для чисел: положительная и отрицательная бесконечность, положительный и отрицательный ноль и NaN, специальный символ для всего, что не является числом.
- строка – представляет собой ноль или более символов, как определено в спецификации XML.
Эти типы данных обычно просты, за исключением наборов узлов, преобразование между типами обычно простое. Мы не будем обсуждать эти типы данных более подробно здесь; вместо этого мы обсудим типы данных и преобразования, поскольку они нам нужны для выполнения определенных задач.
4. Синтаксис XPath
XPath использует синтаксис UNIX и регулярных выражений.
4.1. Выбор узлов с помощью xpath
Выражение | Описание |
---|---|
имя_узла | Выбирает все узлы с именем « nodename » |
/ | Выбирает из корневого узла |
// | Выбирает узлы в документе из текущего узла, которые соответствуют выделению, независимо от того, где они находятся. |
. | Выбирает текущий узел |
.. | Выбирает родительский узел текущего узла |
@ | Выбирает атрибуты |
4.2 Использование предикатов с XPath
Предикаты используются для поиска определенного узла или узла, содержащего определенное значение. Предикаты всегда заключены в квадратные скобки.
Мы узнаем, как их использовать, в следующем разделе.
4.3. Достижение неизвестных узлов с помощью XPath
Для выбора неизвестных элементов XML можно использовать подстановочные знаки XPath.
Универсальный символ | Описание |
---|---|
* | Соответствует любому элементному узлу |
@* | Соответствует любому узлу атрибута |
узел() | Соответствует любому узлу любого типа |
4.4 Оси XPath
Ось определяет набор узлов относительно текущего узла. Ниже приведены оси, определенные по умолчанию.
ИмяОси | Результат |
---|---|
предок | Выбирает всех предков(родителей, прародителей и т. д.) текущего узла. |
предок-или-я | Выбирает всех предков(родителей, прародителей и т. д.) текущего узла и сам текущий узел. |
атрибут | Выбирает все атрибуты текущего узла |
ребенок | Выбирает все дочерние элементы текущего узла |
потомок | Выбирает всех потомков(детей, внуков и т. д.) текущего узла |
потомок-или-я | Выбирает всех потомков(детей, внуков и т. д.) текущего узла и сам текущий узел. |
следующий | Выделяет все в документе после закрывающего тега текущего узла. |
следующий-брат | Выбирает все узлы-братья после текущего узла |
пространство имен | Выбирает все узлы пространства имен текущего узла |
родитель | Выбирает родительский узел текущего узла |
предшествующий | Выбирает все узлы, которые появляются перед текущим узлом в документе, за исключением предков, узлов атрибутов и узлов пространств имен. |
предшествующий брат/сестра | Выбирает все узлы-братья перед текущим узлом |
себя | Выбирает текущий узел |
4.5 Операторы XPath
Ниже приведен список операторов XPath, которые можно использовать в выражениях XPath:
Оператор | Описание | Пример | Возвращаемое значение |
---|---|---|---|
| | Вычисляет два набора узлов | //книга | //компакт-диск | Возвращает набор узлов со всеми элементами book и cd |
+ | Добавление | 6 + 4 | 10 |
— | Вычитание | 6 – 4 | 2 |
* | Умножение | 6 * 4 | 24 |
див | Разделение | 8 дивизия 4 | 2 |
= | Равный | цена=9.80 | правда, если цена 9,80 ложь, если цена 9,90 |
!= | Не равны | цена!=9.80 | правда, если цена 9,90 ложь, если цена 9,80 |
< | Меньше, чем | цена<9.80 | правда, если цена 9.00 ложь, если цена 9,80 |
< = | Меньше или равно | цена< =9.80 | правда, если цена 9.00 ложь, если цена 9,90 |
> | Больше чем | цена>9.80 | правда, если цена 9,90 ложь, если цена 9,80 |
>= | Больше или равно | цена>=9.80 | правда, если цена 9,90 ложь, если цена 9,70 |
или | или | цена=9.80 или цена=9.70 | правда, если цена 9,80 ложь, если цена 9,50 |
и | и | цена>9.00 и цена<9.90 | правда, если цена 9,80 ложь, если цена 8,50 |
мод | Модуль(остаток от деления) | 5 мод 2 | 1 |
5. Как оценивать выражения XPath
Начнем с создания модели DOM для XML-документа следующим образом:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setNamespaceAware(true); // never forget this!DocumentBuilder builder = factory.newDocumentBuilder();Document doc = builder.parse("inventory.xml");
Далее мы создаем экземпляр XPathExpression следующим образом.
XPathFactory xpathfactory = XPathFactory.newInstance();XPath xpath = xpathfactory.newXPath();XPathExpression expr = xpath.compile("//book[@year>2001]/title/text()");
Наконец, мы используем метод XPathExpression.evaluate() для получения результата сопоставления выражения с моделью документа.
Object result = expr.evaluate(doc, XPathConstants.NODESET);NodeList nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}
6. Полный пример
Следующий класс XPathExample оценивает выражения XPath, показанные в начале статьи, и выводит их вывод.
import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.xpath.XPath;import javax.xml.xpath.XPathConstants;import javax.xml.xpath.XPathExpression;import javax.xml.xpath.XPathFactory;import org.w3c.dom.Document;import org.w3c.dom.NodeList;public class XPathExample {public static void main(String[] args) throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setNamespaceAware(true); // never forget this!DocumentBuilder builder = factory.newDocumentBuilder();Document doc = builder.parse("inventory.xml");//Create XPathXPathFactory xpathfactory = XPathFactory.newInstance();XPath xpath = xpathfactory.newXPath();System.out.println("1) Get book titles written after 2001");XPathExpression expr = xpath.compile("//book[@year>2001]/title/text()");Object result = expr.evaluate(doc, XPathConstants.NODESET);NodeList nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("2) Get book titles written before 2001");expr = xpath.compile("//book[@year<2001]/title/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("3) Get book titles cheaper than 8 dollars");expr = xpath.compile("//book[price<8]/title/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("4) Get book titles costlier than 8 dollars");expr = xpath.compile("//book[price>8]/title/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("5) Get book titles added in first node");expr = xpath.compile("//book[1]/title/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("6) Get book title added in last node");expr = xpath.compile("//book[last()]/title/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("7) Get all writers");expr = xpath.compile("//book/author/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("8) Count all books titles");expr = xpath.compile("count(//book/title)");result = expr.evaluate(doc, XPathConstants.NUMBER);Double count =(Double) result;System.out.println(count.intValue());System.out.println("9) Get book titles with writer name start with Neal");expr = xpath.compile("//book[starts-with(author,'Neal')]");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getChildNodes().item(1) //node <title> is on first index.getTextContent());}System.out.println("10) Get book titles with writer name containing Niven");expr = xpath.compile("//book[contains(author,'Niven')]");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getChildNodes().item(1) //node <title> is on first index.getTextContent());}System.out.println("11) Get book titles written by Neal Stephenson");expr = xpath.compile("//book[author='Neal Stephenson']/title/text()");result = expr.evaluate(doc, XPathConstants.NODESET);nodes =(NodeList) result;for(int i = 0; i < nodes.getLength(); i++) {System.out.println(nodes.item(i).getNodeValue());}System.out.println("12) Get count of book titles written by Neal Stephenson");expr = xpath.compile("count(//book[author='Neal Stephenson'])");result = expr.evaluate(doc, XPathConstants.NUMBER);count =(Double) result;System.out.println(count.intValue());System.out.println("13) Reading comment node");expr = xpath.compile("//inventory/comment()");result = expr.evaluate(doc, XPathConstants.STRING);String comment =(String) result;System.out.println(comment);}}
Вывод программы:
1) Get book titles written after 2001Burning Tower2) Get book titles written before 2001Snow CrashZodiac3) Get book titles cheaper than 8 dollarsBurning TowerZodiac4) Get book titles costlier than 8 dollarsSnowz5) Get book titles added in first nodeSnow Crash6) Get book title added in last nodeZodiac7) Get all writersNeal StephensonLarry NivenJerry PournelleNeal Stephenson8) Count all books titles39) Get book titles with writer name start with NealSnow CrashZodiac10) Get book titles with writer name containing NivenBurning Tower11) Get book titles written by Neal StephensonSnow CrashZodiac12) Get count of book titles written by Neal Stephenson213) Reading comment nodeTest is test comment
Мы надеемся, что этот урок по xpath был для вас информативным. Он поможет нам в выполнении xpath с Java. Если у вас есть предложения, пожалуйста, оставьте комментарий.