Рубрики
Без рубрики

Введение в XPath с Java

Быстрое и практическое введение в работу с XPath с помощью стандартных библиотек Java.

Автор оригинала: baeldung.

1. Обзор

В этой статье мы собираемся перейти основы XPath с поддержкой в стандартной Java JDK .

Мы собираемся использовать простой документ XML, обработать его и посмотреть, как перейти к документу, чтобы извлечь из него информацию, которая нам нужна.

XPath является стандартным синтаксисом, рекомендованным W3C, это набор выражений для навигации по документам XML. Вы можете найти полный справочник XPath здесь .

2. Простой XPath Parser

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;

public class DefaultParser {
    
    private File file;

    public DefaultParser(File file) {
        this.file = file;
    }
}

Теперь давайте подробнее рассмотрим элементы, которые вы найдете в По умолчаниюПарсер :

FileInputStream fileIS = new FileInputStream(this.getFile());
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Tutorials/Tutorial";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Давайте разобить, что вниз:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

Мы будем использовать этот объект для создания дерева объектов DOM из нашего документа xml:

DocumentBuilder builder = builderFactory.newDocumentBuilder();

Имея экземпляр этого класса, мы можем анализировать документы XML из различных источников ввода, таких как ВходНая трансляция , Файл , URL и SAX :

Document xmlDocument = builder.parse(fileIS);

Документ ( org.w3c.dom.Document ) представляет весь документ XML, является корнем дерева документов, обеспечивает наш первый доступ к данным:

XPath xPath = XPathFactory.newInstance().newXPath();

С объекта XPath мы будем доступ к выражениям и выполнять их над нашим документом, чтобы извлечь из него то, что нам нужно:

xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Мы можем составить выражение XPath, передаваемое как строка, и определить, какие данные мы ожидаем получить NODESET , NODE или Струнные Например.

3. Позволяет начать

Теперь, когда мы взглянули на базовые компоненты, которые мы будем использовать, давайте начнем с некоторых кодов, используя некоторые простые XML, для целей тестирования:



    
        Guava
  Introduction to Guava
  04/04/2016
  GuavaAuthor
    
    
        XML
  Introduction to XPath
  04/05/2016
  XMLAuthor
    

3.1. Получить базовый список элементов

Первый метод заключается в простом использовании выражения XPath для получения списка узлов из XML:

FileInputStream fileIS = new FileInputStream(this.getFile());
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Tutorials/Tutorial";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

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

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

3.2. Извлечение конкретного узла по его идентификатору

Мы можем искать элемент, основанный на любом идентификаторе, просто отфильтровав:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(this.getFile());
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Tutorials/Tutorial[@tutId=" + "'" + id + "'" + "]";
node = (Node) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODE);

Используя такого рода выражения, мы можем фильтровать любой элемент, который нам нужно искать, используя правильный синтаксис. Такого рода выражения называются предикатами, и они являются простым способом найти конкретные данные по документу, например:

/Tutorials/Tutorial

/Tutorials/Tutorial (первый))

/Tutorials/Tutorial-позиция ()<4″

Вы можете найти полную ссылку на предикаты здесь

3.3. Извлечение узлов по определенному имени тега

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

Document xmlDocument = builder.parse(this.getFile());
this.clean(xmlDocument);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "//Tutorial[descendant::title[text()=" + "'" + name + "'" + "]]";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

С выражением, используемым выше, мы ищем каждую элемент, который имеет потомка текст прошел в качестве параметра в переменной “имя”.

После образца xml, предусмотренных для этой статьи, мы могли бы искать содержащий текст “Гуава” или “XML”, и мы будем получать всю со всеми его данными.

Оси обеспечивают очень гибкий способ навигации по документу XML, и вы можете найти полную документацию, которую он официальный сайт .

3.4. Манипулирование данными в выражениях

XPath позволяет нам манипулировать данными тоже в выражениях, если это необходимо.

XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "//Tutorial[number(translate(date, '/', '')) > " + date + "]";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

В этом выражении мы переходим к нашему методу простую строку в качестве даты, которая выглядит как “ddmmyyyyyy”, но XML хранит эти данные с форматом ” dd/mm/yyyy , таким образом, чтобы соответствовать результату, мы манипулируем строкой, чтобы преобразовать ее в правильный формат данных, используемый нашим документом, и мы делаем это, используя одну из функций, предоставляемых XPath

3.5. Извлечение элементов из документа с определенным пространством имен

Если наш xml документ имеет пространство имен определяется как это в example_namespace.xml, используемых здесь, правила для получения данных, которые нам нужны будут меняться, так как наш xml начинается так:




Теперь, когда мы используем выражение, похожее на ” Tutoria l», мы не получим никакого результата. Это выражение XPath собирается вернуть все , которые не находятся под каким-либо пространством имен, и в нашем новом example_namespace.xml, все элементы определяются в пространстве имен /full_archive .

Давайте посмотрим, как обрабатывать пространства имен.

Прежде всего, нам нужно установить контекст пространства имен, чтобы XPath мог знать, где мы ищем наши данные:

xPath.setNamespaceContext(new NamespaceContext() {
    @Override
    public Iterator getPrefixes(String arg0) {
        return null;
    }
    @Override
    public String getPrefix(String arg0) {
        return null;
    }
    @Override
    public String getNamespaceURI(String arg0) {
        if ("bdn".equals(arg0)) {
            return "/full_archive";
        }
        return null;
    }
});

В методе выше, мы определяем ” bdn ” как название для нашего пространства имен ” /full_archive “, и отныне, мы должны добавить ” bdn ” к выражениям XPath, используемым для поиска элементов:

String expression = "/bdn:Tutorials/bdn:Tutorial";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Используя выражение выше, мы можем получить все элементы под ” bdn ” пространство имен.

3.6. Избежание проблем с пустыми текстовыми узлами

Как вы могли заметить, в коде в разделе 3.3 этой статьи новая функция называется сразу после разбора нашего XML на объект документа, этот .clean ( xmlDocument );

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

Мы позвонили узел .getFirstChild() когда мы итерируя над всеми элементы ищут , но вместо того, что мы ищем, у нас просто есть #Text” в качестве пустого узла.

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

NodeList childs = node.getChildNodes();
for (int n = childs.getLength() - 1; n >= 0; n--) {
    Node child = childs.item(n);
    short nodeType = child.getNodeType();
    if (nodeType == Node.ELEMENT_NODE) {
        clean(child);
    }
    else if (nodeType == Node.TEXT_NODE) {
        String trimmedNodeVal = child.getNodeValue().trim();
        if (trimmedNodeVal.length() == 0){
            node.removeChild(child);
        }
        else {
            child.setNodeValue(trimmedNodeVal);
        }
    } else if (nodeType == Node.COMMENT_NODE) {
        node.removeChild(child);
    }
}

Делая это, мы можем проверить каждый тип узла мы находим и удалить те, которые нам не нужны.

4. Выводы

Здесь мы только что представили по умолчанию XPath при условии поддержки, но Есть много популярных библиотек, как JDOM, Саксонский, X-Квери, JAXP, Jaxen или даже Джексон сейчас. Есть библиотеки для конкретного html разбора тоже, как JSoup.

Это не ограничивается Java, XPath выражения могут быть использованы языком XSLT для навигации XML документов.

Как вы можете видеть, существует широкий спектр возможностей о том, как обрабатывать такого рода файлы.

Существует большая стандартная поддержка по умолчанию для XML/HTML документов разбора, чтения и обработки. Вы можете найти полный рабочий образец здесь .