xmlint не может правильно запросить xpath

Я пытаюсь запросить файл xml, сгенерированный adium. xmlwf говорит, что он правильно сформирован. Используя параметр отладки xmllint, я получаю следующее:

$ xmllint --debug doc.xml
DOCUMENT
version=1.0
encoding=UTF-8
URL=doc.xml
standalone=true
  ELEMENT chat
    default namespace href=http://purl.org/net/ulf/ns/0.4-02
    ATTRIBUTE account
      TEXT
        [email protected]
    ATTRIBUTE service
      TEXT compact
        content=MSN
    TEXT compact
      content= 
    ELEMENT event
      ATTRIBUTE type

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

$ xmllint --xpath '/chat' doc.xml 
XPath set is empty

Что происходит? Выполнение того же самого запроса с использованием xpath возвращает правильные результаты (однако без новой строки между результатами). Я что-то делаю не так или xmlint просто не работает должным образом?

Вот более короткая анонимная версия xml, которая показывает то же поведение:

<?xml version="1.0" encoding="UTF-8" ?>
<chat xmlns="http://purl.org/net/ulf/ns/0.4-02" account="[email protected]" service="MSN">
<event type="windowOpened" sender="[email protected]" time="2011-11-22T00:34:43-03:00"></event>
<message sender="[email protected]" time="2011-11-22T00:34:43-03:00" alias="foo"><div><span style="color: #000000; font-family: Helvetica; font-size: 12pt;">hi</span></div></message>
</chat>

person ailnlv    schedule 25.11.2011    source источник
comment
не могли бы вы поделиться файлом doc.xml   -  person rpg    schedule 25.11.2011


Ответы (3)


Я не использую xmlint, но думаю, что ваш XPath не работает потому, что ваш файл doc.xml использует пространство имен по умолчанию (http://purl.org/net/ulf/ns/0.4-02).

Насколько я вижу, у вас есть 2 варианта.

A. Используйте xmllint в режиме оболочки и объявите пространство имен с префиксом. Затем вы можете использовать этот префикс в своем XPath.

    xmllint --shell doc.xml
    / > setns x=http://purl.org/net/ulf/ns/0.4-02
    / > xpath /x:chat

B. Используйте local-name() для сопоставления имен элементов.

    xmllint --xpath /*[local-name()='chat']

Вы также можете использовать namespace-uri()='http://purl.org/net/ulf/ns/0.4-02' вместе с local-name(), чтобы вы точно вернули то, что собираетесь вернуть.

person Daniel Haley    schedule 25.11.2011
comment
Обратите внимание, что примеры A. и B. завершатся ошибкой, если вы не обращаетесь к корневому пути, и в этом случае вам понадобится двойная косая черта, например, xmllint --xpath //*[local-name()='chat']. См. stackoverflow.com/questions/27311314/ - person the_yellow_logo; 05.12.2014
comment
@Avt'W - этот вопрос/ответ конкретно касается пространств имен в xmllint; а не какие-либо другие темы XPath. То, что соответствует / и //, совершенно не связано. - person Daniel Haley; 06.12.2014
comment
Эй, это был комментарий для читателя, у которого был бы немного другой вариант использования, а не критика вашего ответа, который точно отвечает на проблему. Люди, имеющие проблемы с пространствами имен, вероятно, новички, и поэтому я подумал, что стоит указать на это. - person the_yellow_logo; 07.12.2014
comment
Наблюдение @Avt'W было очень полезным советом для нас, новичков. @daniel-haley Спасибо за подсказку. Вот как я думаю, будет выглядеть полная линия. xmllint --xpath "//*[local-name()='chat' and namespace-uri()='http://purl.org/net/ulf/ns/0.4-02']" - person Greg Elin; 08.06.2015
comment
NB. Это может очень быстро стать запутанным и длинным. В этой статье есть хорошее руководство по этому вопросу; Например, namespace-uri() необходимо добавить к каждой части пути, которая в нем нуждается. - person Dawngerpony; 10.09.2015
comment
Интересно, почему они сделали опцию оболочки setrootns для регистрации всех пространств имен из объявления корневого узла, но не в режиме CLI :( - person Dima Fomin; 09.08.2017
comment
Не то чтобы анализ XML с помощью sed — лучшая идея в мире, но регулярное выражение может быть слишком жадным. Чтобы удалить объявления пространств имен, не вынимая больше, чем вы хотели, используйте sed 's/xmlns="[^"]*"//g'. - person Ken; 13.10.2017

Я понимаю, что этот вопрос очень старый, но на случай, если он кому-то поможет...

Была та же проблема, и это было связано с тем, что XML имел пространство имен (иногда оно дублировалось в разных местах XML). Было проще просто удалить пространство имен перед использованием xmllint:

sed -e 's/xmlns="[^"]*"//g' file.xml | xmllint --xpath "..." -

В моем случае XML был UTF-16, поэтому мне пришлось сначала преобразовать в UTF-8 (для sed):

iconv -f utf16 -t utf8 file.xml | sed -e 's/encoding="UTF-16"?>/encoding="UTF-8"?>/' | sed -e 's/xmlns="[^"]*"//g' | xmllint --xpath "..." -
person codesniffer    schedule 10.04.2019

Если вам разрешено устанавливать powershell в вашей среде (он также доступен для Linux), вы можете сделать это следующим образом:

Select-Xml -XPath '/ns:chat' -Namespace $Namespace .\doc.xml | foreach { $_.Node }
   xmlns   : http://purl.org/net/ulf/ns/0.4-02
   account : [email protected]
   service : MSN
   event   : event
   message : message

Конечно, здесь применяются все те же правила для xpath. Чтобы получить доступ к текстовому содержимому узла:

Select-Xml -XPath '/ns:chat/ns:message' -Namespace $Namespace .\doc.xml |foreach {$_.Node.InnerXML }
<div xmlns="http://purl.org/net/ulf/ns/0.4-02"><span style="color: #000000; font-family: Helvetica; font-size: 12pt;">hi</span></div>

Или содержимое атрибута отправителя:

Select-Xml -XPath '/ns:chat/ns:message/@sender' -Namespace $Namespace .\doc.xml |foreach {$_.Node }

#text
-----
[email protected]
person Juan Carlos Muñoz    schedule 29.04.2021