次のエントリを含む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
これらのノード(例では1つのみ)が与えられると、class
値属性を持つすべてのノードに対して繰り返し検索されますNAME
。
各ノードについて、そのノードの値を抽出します。
文字列OYSTER
とNAME
viaオプションはどちらもコマンドラインの内部変数にバインドされています--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:title
OYSTER
span
class
NAME