如果我有一个包含这样的条目的 xml 文件
<root>
<d:entry d:title="OYSTER">
<span class="foot">
<span role="text">
foo</span>
</span>
<span class="sg">
<span id="004">
<span role="text">
<span class="pos">
<span class="baz">tart</span>
<d:pos></d:pos>
</span>
</span>
<span id="005" class="star">
<span class="NAME">GUYBRUSH THREEPWOOD
<d:def></d:def></span>
<span role="text" class="bar">:</span>
<span role="text" class="grog">
<span class="ex">pirate
</span>
<span class="parrot">.</span>
</span>
</span>
</span>
</span>
</d:entry>
</root>
如何通过提供 (d:) 标题“OYSTER”和类“NAME”来提取文本“GUYBRUSH THREEPWOOD”?
答案1
使用xq
(YAML、XML 和 TOML 的类似解析器集合的一部分,yq
来自jq
https://kislyuk.github.io/yq/),因为xmlstarlet
对缺少的名称空间声明过于严格(xmlstarlet
无论如何请参阅问题末尾的解决方案)。
xq -r --arg title "OYSTER" --arg class "NAME" '
(.. | select(."@d:title"? == $title)) |
(.. | select(."@class"? == $class))."#text"' file.xml
这会递归地选择具有值为 的属性(表达式中使用的d:title
首字母表示节点的属性而不是节点的名称)的任何文档节点。@
OYSTER
给定这些节点(示例中只有一个),将递归搜索它们以查找具有class
value 属性的任何节点NAME
。
对于每个这样的节点,提取该节点的值。
这两个字符串OYSTER
和NAME
通过选项绑定到命令行上的内部变量--arg
。
给出问题中的文档的输出:
GUYBRUSH THREEPWOOD
如果其他节点d:entry
可以具有d:title
属性,和/或其他节点span
可以具有class
属性,并且您希望避免在错误类型的节点中匹配这些属性,那么请确保您只查看适当的节点:
xq -r --arg title "OYSTER" --arg class "NAME" '
(.. | ."d:entry"? | select(."@d:title"? == $title)) |
(.. | .span?[]? | select(."@class"? == $class))."#text"' file.xml
作为参考,由于xq
实际上是jq
在幕后使用 JSON 文档进行调用,因此以下是 XML 文档转换为的 JSON 文档:
{
"root": {
"d:entry": {
"@d:title": "OYSTER",
"span": [
{
"@class": "foot",
"span": {
"@role": "text",
"#text": "foo"
}
},
{
"@class": "sg",
"span": {
"@id": "004",
"span": [
{
"@role": "text",
"span": {
"@class": "pos",
"span": {
"@class": "baz",
"#text": "tart"
},
"d:pos": null
}
},
{
"@id": "005",
"@class": "star",
"span": [
{
"@class": "NAME",
"d:def": null,
"#text": "GUYBRUSH THREEPWOOD"
},
{
"@role": "text",
"@class": "bar",
"#text": ":"
},
{
"@role": "text",
"@class": "grog",
"span": [
{
"@class": "ex",
"#text": "pirate"
},
{
"@class": "parrot",
"#text": "."
}
]
}
]
}
]
}
}
]
}
}
}
假设文档具有正确的d
命名空间声明,xmlstarlet
可用于提取所需的文本,如下所示:
xmlstarlet sel -t \
-m '//d:entry[@d:title = "OYSTER"]' \
-v '//span[@class = "NAME"]' -nl file.xml
或者,在命令行上设置内部变量--var
(注意值中包含引号),
xmlstarlet sel -t --var title='"OYSTER"' --var class='"NAME"' \
-m '//d:entry[@d:title = $title]' \
-v '//span[@class = $class]' -nl file
这两者都从匹配属性d:entry
为 的任何节点开始。对于每个这样的匹配节点,它递归地查找具有 value 属性的节点。输出每个这样的节点的值。d:title
OYSTER
span
class
NAME