Выберите пространство имен по умолчанию в XPath с помощью HtmlUnit - PullRequest
2 голосов
/ 24 мая 2011

Я хочу проанализировать канал Feedburner с помощью HtmlUnit.Вот этот фид: http://feeds.feedburner.com/alcoanewsreleases

Из этого фида я хочу прочитать все item узлы, поэтому обычно //item XPath должен делать свое дело.К сожалению, это не работает в этом случае.

фрагмент кода groovy:

def page = webClient.getPage("http://feeds.feedburner.com/alcoanewsreleases")
def elements = page.getByXPath("//item")

Пример XML-канала:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss1full.xsl"?>
<?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?>

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

[...SNIP...]

<item rdf:about="http://www.alcoa.com/global/en/news/news_detail.asp?newsYear=2011&amp;pageID=20110518006002en">
    <title>Chris L. Ayers Named President, Alcoa Global Primary Products</title>
    <dc:date>2011-05-18</dc:date
    <link>http://feedproxy.google.com/~r/alcoanewsreleases/~3/PawvdhpJrkc/news_detail.asp</link>
    <description>NEW YORK--(BUSINESS WIRE)--Alcoa (NYSE:AA) announced today that Chris L. Ayers has been named President of Alcoa’s Global Primary Products (GPP) business, effective May 18, 2011. Ayers, previously Chief Operating Officer of GPP, succeeds John Thuestad, who will be handling special projects for the Company. Ayers joined Alcoa in February 2010 as Chief Operating Officer of Alcoa Cast, Forged and Extruded Products, a new position. He was elected a Vice President of Alcoa in April 2010 and Executive</description>
    <feedburner:origLink xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://www.alcoa.com/global/en/news/news_detail.asp?newsYear=2010&amp;pageID=20100104006194en</feedburner:origLink>
</item>

[...SNIP...]

</rdf:RDF>

Я подозреваю, что это проблемас пространствами имен, потому что этот документ имеет 4 пространства имен.Пространства имен:

Я пытался использовать Nokogiri с этим (еще один анализатор XML, который я использую для сценариев ruby).С Nokogiri я мог просто использовать XPath //xmlns:item, который работает и возвращает все узлы из канала.

Я пробовал тот же XPath с HtmlUnit, но он не работает.

Так что я думаю, что могу сформулировать свой вопрос следующим образом: Как я могу выбрать узел из пространства имен по умолчанию с помощью HtmlUnit?

Есть идеи?

Ответы [ 2 ]

4 голосов
/ 24 мая 2011

Из этого канала я хочу прочитать все item узлы, поэтому обычно //item XPath должен сделать свое дело. к несчастью это не работает в этом случае.

В XPath это означает «выбрать все элементы с локальным именем item , которые не находятся в пространстве имен ». В RSS элементы item должны находиться в пространстве имен. Поэтому вышеприведенное никогда не должно работать с соответствующим синтаксическим анализатором XML и механизмом XPath.

Что сбивает с толку, так это то, что в XML <item> означает «элемент с именем item, который находится в пространстве имен default , т. Е. Любое пространство имен по умолчанию находится в области действия в этом месте документа;» тогда как в XPath «item» означает элемент в no пространстве имен. (Или, можно сказать, это означает элемент в пространстве имен по умолчанию, но если у вас нет способа сообщить XPath, что такое пространство имен по умолчанию, пространство имен по умолчанию не является пространством имен. Обычно (всегда?) В XPath 1.0 нет никакого способа объявить пространство имен по умолчанию для выражений XPath.)

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

С Нокогири я мог только нас XPath //xmlns:item, который работает и возвращает все узлы из канала.

Что бы это ни было, это не XPath. Может быть, это расширение к Nokogiri (очень удобное, но его синтаксис действительно нелогичный).

Так что я думаю, что могу сформулировать свой вопрос Как: Как я могу выбрать узел из пространство имен по умолчанию с HtmlUnit?

Давайте сформулируем это следующим образом: Как я могу выбрать элементы элемента RSS с помощью HtmlUnit? Я формулирую это так, потому что спецификация RSS (фактически вообще любая соответствующая спецификация словаря XML) не требует , что ее элементы будут в пространстве имен по умолчанию. Это верно для образца, который вы получили, но поставщик услуг может изменить это завтра и при этом полностью соответствовать RSS. Завтра поставщик услуг может использовать префикс пространства имен «rss» для этого пространства имен; или любой другой произвольный префикс. Что RSS делает , указывает, в каком пространстве имен будут его элементы: пространство имен, URI которого http://purl.org/rss/1.0/.

Это все равно что спрашивать: «Как мне написать функцию (в Javascript, C, Java и т. Д.), Которая может сообщить мне значение переменной a?» Обычно функция не имеет представления, какое имя переменной использовалось для чего в вызывающей программе. Все, что он знает, это значения своих аргументов. Если вы позвоните sqrt(4), вы получите тот же ответ, что и с a = 4; sqrt(a) или rumpelstiltzkin = 4; sqrt(rumpelstiltzkin). Ясно, что имя аргумента переменной не имеет прямого влияния на результат вызова функции. Это просто должно быть имя переменной, которая содержит правильное значение. Если компилятор жаловался на то, что вы написали b = 4; return sqrt(b) вместо использования a, вы бы подумали, что компилятор был чокнутым. Он не должен заботиться об именах переменных, если вы используете допустимые идентификаторы.

Точно так же при обработке RSS мы не должны заботиться о том, какой префикс пространства имен используется, если он является префиксом, определяющим правильное пространство имен. Это может быть без префикса (который определяет пространство имен по умолчанию).

В XPath 2.0 вы можете использовать подстановочные знаки для пространства имен. Это очень удобно, если вы знаете, что вам не понадобятся пространства имен для устранения неоднозначности. В этом случае вы можете выбрать //*:item. Однако я не думаю, что HTMLUnit поддерживает XPath 2.0. Также в средах XPath 2.0, таких как XSLT 2.0, вы можете указать пространство имен по умолчанию для выражений XPath, но это не поможет вам в HTMLUnit.

Итак, у вас есть пара вариантов:

  • Используйте выражение XPath, которое игнорирует пространства имен, например //*[local-name() = 'item'].

или

  • Надежный способ: зарегистрируйте префикс пространства имен для http://purl.org/rss/1.0/ и используйте его в своем выражении XPath: //rss:item. Тогда возникает вопрос: как зарегистрировать префикс пространства имен в HTMLUnit и передать его процессору XPath? Я быстро просмотрел документы и не нашел возможности сделать это.

Предостережение: Я должен добавить, что вышесказанное относится к соответствующим процессорам XPath. Я понятия не имею, что процессор XPath HTMLUnit использует. Есть некоторые процессоры XPath, которые игнорируют спецификации и делают мир более запутанным для всех.

Я видел здесь , что кто-то использовал следующий синтаксис для элементов в пространстве имен по умолчанию в HTMLUnit:

//:item

Но я бы не рекомендовал этого по трем причинам:

  1. Это недопустимый XPath, поэтому вы не можете ожидать, что он будет работать с другими программами.

  2. Он будет работать только с RSS-лентами, в которых пространство имен RSS объявлено пространством имен по умолчанию. RSS-каналы, использующие префикс пространства имен, приведут к сбою вышеуказанного.

  3. Это удержит вас от изучения того, как реально работают пространства имен XML, и поможет сохранить статус-кво инструментов, которые не поддерживают пространства имен должным образом.

HTMLUnit в первую очередь предназначен для HTML, поэтому понятна неполная обработка XML. Но заявление о поддержке XPath, а затем об отсутствии способа объявления префиксов пространства имен является ошибкой . HTMLUnit использует пакет XPath, который кажется частью Xalan-J. В этом пакете есть способов предоставления сопоставлений пространства имен для XPath , но я не знаю, предоставляет ли HTMLUnit эту функциональность.

0 голосов
/ 24 мая 2011

Это звучит достаточно знакомо, и я уверен, что в прошлом я успешно использовал пространства имен и XPath с HtmlUnit, но, конечно, я не могу найти код.Я подозреваю, что это должно быть только с HTML-страницами: ссылка page в вашем примере это XmlPage, которая имеет ряд методов, специфичных для пространств имен, каждый из которых выдает «еще не реализовано»исключение при использовании.: - (

Текущей версии (2.8) HtmlUnit уже почти год, поэтому может случиться так, что за это время была проделана некоторая работа по поддержке пространств имен XML.список было бы местом, чтобы узнать.

Между тем, как всегда, есть обходной путь:

final XmlPage page = webClient.getPage("http://feeds.feedburner.com/alcoanewsreleases");

// no good
List elements = page.getByXPath("//item");
System.out.println( elements.size() ) ;

// ugly, but it works
DomElement de = (DomElement)page.getFirstByXPath( "//rdf:RDF" );
List<DomNode> items = new ArrayList<DomNode>() ;
for( DomNode dn : de.getChildNodes() )
{
    String name = dn.getLocalName() ;
    if( ( name != null ) && ( name.equals( "item" ) ) )
        items.add( dn ) ;
}
System.out.println( "found " + items.size() ) ;

Ох, боже, ява болезненна после работы в Scala ...; -)

...