JDOM2 с XPath не работает с пространством имен

#java #xml #xpath #jdom-2

#java #xml #xpath #jdom-2

Вопрос:

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

Пример кода:

     final SAXBuilder builder = new SAXBuilder();
    final Document document = builder.build(originalFile);

    final XPathFactory xFactory = XPathFactory.instance();

    final String expression = String.format("//section[@label='%s']/section/section", LABEL);
    final XPathExpression<Element> sectionExpression = xFactory.compile(expression, Filters.element());
    final List<Element> sections = sectionExpression.evaluate(document);
  

Разделы пусты.

Фрагмент XML-

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://www.stellent.com/sitestudio/Project/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.stellent.com/sitestudio/Project/ http://www.stellent.com/sitestudio/ss_project_schema.xsd">
    <section label="Index">
        <section label="xyz">
           <section label="child">
           ...
           </section>
        </section>
    </section>
</project>
  

Удаление xmlns="http://www.stellent.com/sitestudio/Project/" работает, но не является решением!

Почему XPath не может узнать об этом пространстве имен по умолчанию? Кроме того, почему это волнует?

А еще лучше, как я могу это исправить в общем виде?

Спасибо за любую информацию.

Комментарии:

1. Обратите внимание, что XPath 2.0 позволяет вам определить пространство имен по умолчанию, которое применяется к именам элементов без префиксов в вашем выражении XPath; но XPath API по умолчанию в JDOM2 использует XPath 1.0. Если вы хотите использовать XPath 2.0 с JDOM2, вы можете сделать это с помощью движка Saxon XPath.

Ответ №1:

JDOM поступает правильно. Это часто задаваемые вопросы, и не только для JDOM, но и для XPath в целом. Спецификация XPath (в некоторой степени) ясна по этому поводу (я выделил соответствующую часть жирным шрифтом):

QName в тесте узла расширяется до расширенного имени с использованием объявлений пространства имен из контекста выражения. Таким же образом выполняется расширение для имен типов элементов в тегах start и end за исключением того, что пространство имен по умолчанию, объявленное с помощью xmlns, не используется: если QName не имеет префикса, то URI пространства имен равен null (таким же образом расширяются имена атрибутов). Это ошибка, если QName имеет префикс, для которого нет объявления пространства имен в контексте выражения.

С точки зрения XPath, это означает, что обработка пространства имен для правил в выражении XPath на самом деле не совпадает с узлами в XML. Для всех выражений XPath вам необходимо определить (дублировать) контекст пространства имен для выражения, и префиксы, которые вы используете для выражения, фактически полностью независимы от тех, которые используются в реальном XML-документе.

Вам нужно «изобрести» префикс пространства имен для вашего пространства имен по умолчанию и использовать этот префикс в вашем выражении (здесь я изобрел префикс пространства имен ns ):

 final String expression = String.format("//ns:section[@label='%s']/ns:section/ns:section", LABEL);
final XPathExpression<Element> sectionExpression = xFactory.compile(expression, Filters.e, null,
         Namespace.getNamespace("ns", "http://www.stellent.com/sitestudio/Project/"))
  

Комментарии:

1. Я все еще не до конца понимаю это .. но это работает.. спасибо! 🙂

2. Привет, @ThomasBeauvais — я провожу много времени в комнате чата 2nd monitor stack exchange . Я буду рад рассмотреть это более подробно.

3. Есть ли способ настроить это так, чтобы существовало какое-то пространство имен по умолчанию? Так что, префикс не требуется?

4. @ycomp — для XPath ответ «Нет», и это по дизайну XPath. Это несоответствие между XML и XPath, которое застало многих людей врасплох, но обойти его невозможно. Это часть спецификации XPath.

5. @rolfl спасибо, по крайней мере, теперь я это знаю.. Я провел некоторое время, ударяясь головой о клавиатуру, пытаясь разобраться в этом. Теперь я просто использую префикс из 1 буквы