学习解析 xml 通常被认为是一件复杂的事情,但它不一定是这样。xml 是高度严格结构化的,所以也是相对来说可预测的。也有许多其他工具可以帮助你使这项工作易于管理。

我最喜欢的 xml 实用程序之一是 xmlstarlet,这是一个用于终端的 xml 工具包,借助这个 xml 工具包,你可以验证、解析、编辑、格式化和转换 xml 数据。xmlstarlet 是个相对较小的命令,但浏览 xml 却充满潜力,因此本文演示了如何使用它来查询 xml 数据。

安装

xmlstarlet 默认安装在 centos、fedora,和许多其他现代 linux 发行版上,所以你可以打开终端,输入 xmlstarlet来访问它。如果 xmlstarlet 还没有被安装,你的操作系统则会为你安装它。

或者,你可以用包管理器安装 xmlstarlet

$ sudo dnf install xmlstarlet

在 macos 上,可以使用 macports或homebrew。在 windows 上,可以使用chocolatey。

如果都失败了,你可以从 sourceforge 上的源代码手动安装它。

用 xmlstarlet 解析 xml

有许多工具可以帮助解析和转换 xml 数据,包括允许你 编写自己的解析器的软件库,和复杂的命令,如fopxsltproc。不过有时你不需要处理 xml 数据;你只需要一个方便的方法从 xml 数据中来提取、更新或验证重要数据。对于随手的 xml 交互,我使用xmlstarlet,这是常见的处理 xml任务的一个典型的“瑞士军刀”式应用。通过运行--help命令,你可以看到它提供哪些选项:

$ xmlstarlet --help
usage: xmlstarlet [

你可以通过在这些子命令的末尾附加 -help来获得进一步的帮助:

$ xmlstarlet sel --help
  -q or --quiet             - do not write anything to standard output.
  -c or --comp              - display generated xslt
  -r or --root              - print root element <xsl-select>
  -t or --text              - output is text (default is xml)
  -i or --indent            - indent output
[...]

用 sel 命令选择数据

可以使用 xmlstarlet select(简称sel)命令查看 xml 格式的数据。下面是一个简单的 xml 文档:


在 xml 文件中查找数据时,你的第一个任务是关注要探索的节点。如果知道节点的路径,请使用 -value of选项指定完整路径。你越早浏览文档对象模型(dom)树,就可以看到更多信息:

$ xmlstarlet select --template \
  --value-of /xml/os/linux/distribution \
  --nl myfile.xml
      fedora
      7
      moonshine
     
        live
        fedora
        everything     
     
      fedora core
      6
      zod

--nl代表“新的一行”,它插入大量的空白,以确保在输入结果后,终端在新的一行显示。我已经删除了样本输出中的一些多余空间。

通过进一步深入 dom 树来凝聚关注点:

$ xmlstarlet select --template \
  --value-of /xml/os/linux/distribution/name \
  --nl myfile.xml
fedora
fedora core

条件选择

用于导航和解析 xml 的最强大工具之一被称为 xpath。它规范了 xml 搜索中使用的语法,并从 xml 库调用函数。xmlstarlet 能够解析 xpath 表达式,因此可以使用 xpath 函数来有条件的进行选择。xpath 具有丰富的函数,,但我觉得 mozilla 的 xpath 文档更简洁。

可以使用方括号作为测试函数,将元素的内容与某个值进行比较。下面是对 元素的值的测试,它仅返回与特定匹配相关联的版本号。

想象一下,示例 xml 文件包含以 1 开头的所有 fedora 版本。要查看与旧名称 “fedora core” 关联的所有版本号(该项目从版本 7 开始删除了名称中的 “core”),请执行以下操作:

$ xmlstarlet sel --template \
  --value-of '/xml/os/linux/distribution[name = "fedora core"]/release' \
  --nl myfile.xml
6
5
4
3
2
1

通过将路径的 --value-of更改为
/xml/os/linux/distribution[name=“fedora core”]/codename
,你便可以查看这些版本的所有代号。

匹配路径和获取目标值

将 xml 标记视为节点的一个好处是,一旦找到节点,就可以将其视为当前的数据的“目录”。它不是一个真正的目录,至少不是文件系统意义上的目录,但它是一个可以查询的数据集合。为了帮助你将目标和“里面”的数据分开,xmlstarlet 把你试图用 --match选项匹配的内容和用--value-of选项匹配的数据值进行了区分。

假设你知道 节点包含几个元素。这就是你的目标节点。一旦到了这里,就可以使用 --value-of指定想要哪个元素的值。要查看所有元素,可以使用点(.)来代表当前位置:

$ xmlstarlet sel --template \
  --match '/xml/os/linux/distribution/spin' \
  --value-of '.' --nl myfile.xml \
live
fedora
everything

与浏览 dom 一样,可以使用 xpath 表达式来限制返回数据的范围。在本例中,我使用 last函数来检索spin节点中的最后一个元素:

$ xmlstarlet select --template \
  --match '/xml/os/linux/distribution/spin' \
  --value-of '*[last()]' --nl myfile.xml
everything

在本例中,我使用 position函数选择spin节点中的特定元素:

$ xmlstarlet select --template \
  --match '/xml/os/linux/distribution/spin' \
  --value-of '*[position() = 2]' --nl myfile.xml
fedora

--match--value选项可以重叠,因此如何将它们一起使用取决于你自己。对于示例 xml,这两个表达式执行的是相同的操作:

$ xmlstarlet select  --template \
  --match '/xml/os/linux/distribution/spin' \
  --value-of '.' \
  --nl myfile.xml
live
fedora
everything     

$ xmlstarlet select --template \
  --match '/xml/os/linux/distribution' \
  --value-of 'spin' \
  --nl myfile.xml
live
fedora
everything

熟悉 xml

xml 有时看起来过于冗长和笨拙,但为与之交互和构建的工具却总是让我吃惊。如果你想要好好使用 xml,那么 xmlstarlet 可能是一个很好的切入点。下次要打开 xml 文件查看其结构化数据时,请尝试使用 xmlstarlet,看看是否可以查询这些数据。当你对 xml 越熟悉时,它就越能作为一种健壮灵活的数据格式而为你服务。