Как использовать name () и / или node () в запросе Java XPath?

1

У меня есть XML, который выглядит примерно так:

<project type="mankind">
    <suggestion>Build the enterprise</suggestion>
    <suggestion>Learn Esperanto</suggestion>
    <problem>Solve world hunger</suggestion>
    <discussion>Do Vulcans exist</discussion>
</project>

Я хочу использовать XPath, чтобы узнать имена элементов второго уровня (могут быть элементы, которые я не буду знать заранее) с помощью Java. Это код, который я пробовал:

public NodeList xpath2NodeList(Document doc, String xPathString) throws XPathExpressionException {
     XPath xpath = XPathFactory.newInstance().newXPath();
     MagicNamespaceContext nsc = new MagicNamespaceContext();
     xpath.setNamespaceContext(nsc);
     Object exprResult = xpath.evaluate(xPathString, doc, XPathConstants.NODESET);
     return (NodeList) exprResult;
}

Мой XPath is /project/*/name(). Я получаю сообщение об ошибке:

javax.xml.transform.TransformerException: Неизвестный nodetype: name

Запрос типа /project/suggestion работает так, как ожидалось. Что мне не хватает? Я хотел бы получить список с именами тегов.

  • 0
    С какой версией XPath вы работаете?
  • 0
    Вы должны использовать / project / @name name not / project / * / name ()
Показать ещё 2 комментария
Теги:
xpath

2 ответа

2
Лучший ответ

Java6 (не спрашивайте).

Я думаю, что ваша реализация поддерживает только XPath 1.0. Если бы это было так, то работало бы только следующее:

"name(/project/*)"

Причиной этого является то, что в модели XPath 1.0 вы не можете использовать функции (например, name()) как шаг в выражении пути. Ваш код генерирует исключение, и в этом случае процессор ошибочно использует ваше name() функции name() для теста неизвестного узла (например, comment()). Но нет ничего плохого в использовании выражения пути в качестве аргумента функции name().

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

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

С XPath 2.0 ваш внутренний запрос будет в порядке. Также см. Этот связанный вопрос.

  • 0
    Намного лучше, теперь я получаю org.apache.xpath.XPathException: не могу преобразовать #STRING в NodeList !, но я думаю, это из-за моего XPathConstants.NODESET. Когда я пытаюсь сделать это с XPathConstants.STRING, это работает. Теперь .... есть ли способ выяснить, будет ли это только строка или вернется нода. Если я опускаю параметр, то всегда возвращается строка
  • 0
    @stwissel Я не знаком с Java Transformer, но нужно ли, чтобы метод, который оценивает ваше выражение пути, возвращал NodeList ? Можете ли вы написать разные методы, один для поиска узлов, а другой для строк?
Показать ещё 3 комментария
1

Ниже код может ответить на ваш оригинальный вопрос.

    package com.example.xpath;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;

    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.XPathExpression;
    import javax.xml.xpath.XPathExpressionException;
    import javax.xml.xpath.XPathFactory;

    import org.w3c.dom.Document;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import org.xml.sax.SAXException;

    public class XPathReader {

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

        public static void main(String[] args) {

            try {
                FileInputStream file = new FileInputStream(new File("c:/mankind.xml"));

                DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

                DocumentBuilder builder =  builderFactory.newDocumentBuilder();

                Document xmlDocument = builder.parse(file);

                XPathExpression expr = xPath.compile("//project/*");
                NodeList list= (NodeList) expr.evaluate(xmlDocument, XPathConstants.NODESET);
                for (int i = 0; i < list.getLength(); i++) {
                    Node node = list.item(i);
                    System.out.println(node.getNodeName() + "=" + node.getTextContent());
                }

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (XPathExpressionException e) {
                e.printStackTrace();
            }        
        }
    }

Предполагая, что ввод исправлен (разрешите голод в мире), код должен печатать:

предложение = создать предприятие
предложение = Узнать эсперанто
проблема = Решите голод в мире
обсуждение = Существуют ли вулканы

  • 0
    Спасибо за код. Задача здесь: XPath возвращает весь элемент, а не только имя. Мне нужно уметь различать получение имени или содержимого элемента на основе XPath. С возвращенным Nodeset шаг постобработки должен сделать это, но это требует дополнительных «знаний» помимо выражения XPath
  • 0
    На распечатке код показывает, как просто получить имя; то есть node.getNodeName ().
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню