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

これらのノード(例では1つのみ)が与えられると、class値属性を持つすべてのノードに対して繰り返し検索されますNAME

各ノードについて、そのノードの値を抽出します。

文字列OYSTERNAMEviaオプションはどちらもコマンドラインの内部変数にバインドされています--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属性が一致するノードで始まります。一致する各ノードの値属性を持つノードを繰り返し探します。各ノードの値を印刷します。d:titleOYSTERspanclassNAME

おすすめ記事