Использование XPath для извлечения элементов XOM из документов с ненужными пространствами имен - PullRequest
2 голосов
/ 12 марта 2012

Я пытаюсь проанализировать некоторый HTML-код, возвращенный внешней системой с XOM.HTML-код выглядит следующим образом:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<body>
  <div>
    Help I am trapped in a fortune cookie factory
  </div>
</body>
</html>

(На самом деле он значительно более запутанный, но в нем есть объявление DOCTYPE, а также объявления пространств имен и языка, а в приведенном выше коде HTML та же проблема, что и в реальном HTML.)

Я хочу извлечь содержимое <div>, но объявление пространства имен, похоже, сбивает с толку XPath.Если я удаляю объявление пространства имен (вручную, из файла), следующий код находит <div>, без проблем:

Document document = ...
Nodes divs = document.query("//div");

Но с пространством имен возвращаемый Nodes имеет размериз 0.

Хорошо, а как насчет того, чтобы программно обрезать пространство имен?

Element rootElement = document.getRootElement();
rootElement.removeNamespaceDeclaration(rootElement.getNamespacePrefix());

... выглядит так, как будто оно должно работать, но ничего не делает.Из javadoc :

Этот метод удаляет только дополнительные пространства имен, добавленные с addNamespaceDeclaration.

Хорошо, я думал, я предоставлю пространство именк запросу:

XPathContext context = 
    XPathContext.makeNamespaceContext(document.getRootElement());
Nodes divs = document.query("//div", context);

Размер по-прежнему равен нулю.

Как насчет создания контекста пространства имен вручную?

XPathContext context = context = new XPathContext(
     rootElement.getNamespacePrefix(), rootElement.getNamespaceURI());
Nodes divs = document.query("//div", context);

Конструктор XPathContext взрывается с помощью:

nu.xom.NamespaceConflictException: 
    XPath expressions do not use the default namespace

Итак, я ищу:

  1. способ заставить этот запрос работать или
  2. способ программным образом обрезать объявления пространства именили
  3. объяснение правильного подхода, предполагая, что оба они неверны.

Обновление: На основании Ответ Льва Левицкого и Jaxen FAQ Я придумал следующий взлом:

XPathContext context = new XPathContext(
    "foo", 
    document.getRootElement().getNamespaceURI());
Nodes divs = document.query("//foo:div");

Это все еще кажется мне немного сумасшедшим, но я предполагаю, что Джексен хочет, чтобы вы это делаливещи.


Обновление № 2: Как отмечено ниже и по всему Интернету , это не вина Джаксена;просто XPath - это XPath.

Итак, пока этот хак работает, я все же хотел бы отменить объявление пространства имен.Желательно, не доходя до XSLT.

Ответы [ 2 ]

2 голосов
/ 03 апреля 2013

Вы можете написать:

Nodes divs = document.query("//*[local-name()='div' and namespace-uri()='http://www.w3.org/1999/xhtml']");
1 голос
/ 13 марта 2012

Вы должны либо указать пространство имен напрямую с помощью чего-то вроде

Nodes divs = document.query("//{http://www.w3.org/1999/xhtml}div");

, либо использовать префиксы, которые сопоставлены с соответствующими пространствами имен (я думаю, это то, для чего предназначен NamespaceContext, но в вашем префиксе нет префиксовquery).

К сожалению, я не знаю, как это реализовано в Java, но могу предоставить пример Python, если это поможет.

...