【问题标题】:xmllint failing to properly query with xpathxmllint 无法使用 xpath 正确查询
【发布时间】:2012-01-06 00:58:11
【问题描述】:

我正在尝试查询 adium 生成的 xml 文件。 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
        content=foo@bar.com
    ATTRIBUTE service
      TEXT compact
        content=MSN
    TEXT compact
      content= 
    ELEMENT event
      ATTRIBUTE type

一切似乎都解析得很好。但是,当我尝试查询最简单的事情时,我什么也得不到:

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

发生了什么事?使用 xpath 运行完全相同的查询会返回正确的结果(但是结果之间没有换行符)。是我做错了什么还是 xmllint 不能正常工作?

以下是显示相同行为的 xml 的简短匿名版本:

<?xml version="1.0" encoding="UTF-8" ?>
<chat xmlns="http://purl.org/net/ulf/ns/0.4-02" account="foo@bar.com" service="MSN">
<event type="windowOpened" sender="foo@bar.com" time="2011-11-22T00:34:43-03:00"></event>
<message sender="foo@bar.com" 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>

【问题讨论】:

  • 能分享一下doc.xml文件吗

标签: xml xpath xmllint


【解决方案1】:

我不使用 xmllint,但我认为您的 XPath 不起作用的原因是您的 doc.xml 文件使用了默认命名空间 (http://purl.org/net/ulf/ns/0.4-02)。

据我所知,您有两个选择。

A. 在 shell 模式下使用 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() 一起使用,这样您就可以确保返回您想要返回的内容。

【讨论】:

  • 注意示例 A. 和 B. 如果您不访问根路径,将失败,在这种情况下,您需要双斜杠,例如 xmllint --xpath "//*[local-name ()='聊天']"。见stackoverflow.com/questions/27311314/…
  • 嘿,这是给读者的评论,它的用例会略有不同,而不是批评您准确回答问题的答案。命名空间有问题的人可能是新手,因此我认为值得指出这一点。
  • C. cat foo.xml | sed '2 s/xmlns=".*"//g' | xmllint --xpath ...
  • @Avt'W 观察对我们新手来说是非常有用的提示。 @daniel-haley 感谢 shell 提示。这是我认为全线的样子。 xmllint --xpath "//*[local-name()='chat' and namespace-uri()='http://purl.org/net/ulf/ns/0.4-02']"
  • 注意。这很快就会变得混乱和冗长。 This article 有一个很好的教程;例如,namespace-uri() 必须添加到需要它的路径的每个部分。
【解决方案2】:

我意识到这个问题现在已经很老了,但万一它对某人有帮助......

有同样的问题,这是由于 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 "..." -

【讨论】:

  • 这将破坏 XML 文件中的数据。 xmllint 这样的工具的重点是正确解析 XML。
  • 可以直接在文件中为http 命名空间分配一个本地名称,例如xsed -e 's/xmlns=/xmlns:x=/'。然后您可以将您的命令与 xpath 表达式一起使用,例如 //item
【解决方案3】:

如果你被允许在你的环境中安装 powershell(它也适用于 Linux),你可以这样做:

Select-Xml -XPath '/ns:chat' -Namespace $Namespace .\doc.xml | foreach { $_.Node }
   xmlns   : http://purl.org/net/ulf/ns/0.4-02
   account : foo@bar.com
   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>

或者sender属性的内容:

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

#text
-----
foo@bar.com

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    相关资源
    最近更新 更多