从 xml 文件中提取文本

从 xml 文件中提取文本

如果我有一个包含这样的条目的 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来自jqhttps://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

给定这些节点(示例中只有一个),将递归搜索它们以查找具有classvalue 属性的任何节点NAME

对于每个这样的节点,提取该节点的值。

这两个字符串OYSTERNAME通过选项绑定到命令行上的内部变量--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:titleOYSTERspanclassNAME

相关内容