Разбор XML с использованием префиксов пространства имен в синтаксисе для корневых элементов - Java - PullRequest
0 голосов
/ 19 сентября 2018

У меня есть XML в форме:

<?xml version="1.0" encoding="UTF-8"?>
<semseg:Envelope xmlns:semseg="http://a-random-URL" xmlns="http://another-random-URL">
    <semseg:subject>Subject</semseg:subject>
    <semseg:Sender>
        <semseg:name>Me</semseg:name>
    </semseg:Sender>
    <Triangle>
        <Triangle time='2017-11-29'>
            <Triangle key='a' value='b'/>
            <Triangle key='c' value='d'/>
            <Triangle key='e' value='f'/>
            <Triangle key='g' value='h'/>
        </Triangle>
    </Triangle>
</semseg:Envelope>

И я пытаюсь получить элемент <Triangle> ( не <Triangle time='2017-11-29'> - имена элементов немногов этом XML), используя XPath.Часть кода выглядит следующим образом:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse("file.xml");

XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression xpr = xPath.compile("/semseg:Envelope/Triangle");
NodeList nodes = (NodeList)xpr.evaluate(doc, XPathConstants.NODESET);

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

/semseg:Envelope/Triangle/Triangle/@time

Кажется, что есть проблема с префиксами пространства имен.Синтаксический анализ XML s без префиксов пространства имен прекрасно работает с XPath.

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Ваш XML-ввод на самом деле имеет два пространства имен.

Пространство имен по умолчанию

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

<semseg:Envelope ... xmlns="http://another-random-URL" ...

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

пространство имен semseg

Определено так:

<semseg:Envelope xmlns:semseg="http://a-random-URL" ...

Значение каждого элемента XML с префиксом semseg принадлежит этому пространству имен.

Перевод ваших требований

Итак, вы нацелены на выражение XPath, которое будет нацелено на

  • любой элемент Triangle (без префикса, так что фактически преобразуется в любой Triangle элемент из http://another-random-URL пространства имен ).
  • Это прямой дочерний элемент корневого semseg:Enveloppe элемента (который фактически преобразуется в корневой элемент локального имени Enveloppe, принадлежащий пространству имен "http://a -random-URL " ).

Программирование в XPath.

Мы создаем NamespaceContext, который описывает, какие пространства имен мы работаем.ng with: Я определяю префиксы, с которыми я хочу работать, и сопоставляю их с пространствами имен.Эти префиксы будут использоваться движком XPath.Я сопоставляю:

  • Префикс main к пространству имен http://a-random-URL
  • Префикс secondary к пространству имен http://another-random-URL

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

/main:Envelope/secondary:Triangle

И это работает:

XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
xPath.setNamespaceContext(new NamespaceContext() {
    @Override
    public String getNamespaceURI(String prefix) {
        if ("main".equals(prefix)) {
            return "http://a-random-URL";
        }
        if ("secondary".equals(prefix)) {
            return "http://another-random-URL";
        }
        return null;
    }
    @Override
    public String getPrefix(String namespaceURI) {
        // This should be implemented but I'm lazy and this sample works without it
        return null;
    }

    @Override
    public Iterator getPrefixes(String namespaceURI) {
        // This should be implemented but I'm lazy and this sample works without it
        return null;
    }
});
XPathExpression xpr = xPath.compile("/main:Envelope/secondary:Triangle");
NodeList nodes = (NodeList)xpr.evaluate(doc, XPathConstants.NODESET);
System.out.println(nodes.getLength());

Выходы:

1

Здесь яреализовали очень тупой контекст пространства имен, но если у вас есть Spring, CXF, guava (я думаю) или другие доступные платформы, у вас часто есть что-то вроде SimpleNamespaceContext или MapBasedNamespaceContext, которые, вероятно, являются лучшими вариантами.

0 голосов
/ 19 сентября 2018

Это работает для меня

/\*[local-name()='Envelope']/\*[local-name()='Triangle']/\*[local-name()='Triangle']/@time

...