Edited at

jqを使ってJsonの深いネストにあるキーの値を抽出する方法(上の階層のキーがわからない時)

More than 3 years have passed since last update.

jqでJSONを整形してる時に、深いネストのキーを検索する方法のメモ

ただ、この記事はjq歴3時間のド素人書いたやつなので、もっといい方法が絶対あるはず(と言うか誰かおしえてください)


キー名でのフィルターのかけ方

例えば以下のJsonファイルで「key1」というキーを持つ文字列だけを取り出したい場合は以下のように書く。


test.json

{

"key1":{
"key2":"value1",
"key3":"value2"
},
"foo":"bar"
}


Terminal

$ jq '.key1' test.json

{
"key2": "value1",
"key3": "value2"
}

要するに.hogeって書けば、ドットの後に続けた文字列のキーに該当する値を返してくれる。これは連続で書くこともできて、例えば以下のように書くと、ネストを掘りながらフィルターを掛けることができる。


Terminal

$ jq '.key1.key2' test.json

"value1"

ただしこのフィルターは1階層目のキーしか見てくれないので、例えば以下のように書いてしまうとnullが帰ってきてしまう。


Terminal

$ jq '.key2' test.json

null

つまりこのフィルターで値をとってきたい場合、「抽出したいキーの名前はわかってるんだけど、その上の階層でどんなキーが指定されてるのかわからなくて、そもそも何階層目にあるかわからない」って時には上手く動かない。

要するにどんな階層にいても目的のキーを抽出してくれるgrep的な何かがほしいわけです。(まあそれこそgrep使えばいいのかもしれないけど)。


recurseを使う

resurseってのはJsonのオブジェクトの中身を再帰的に展開してくれるフィルター。たぶん見てみるのが一番早いと思う。


test.json

{

"key1":{
"key2":"value1",
"key3":"value2"
},
"foo":"bar"
}


Terminal

$ jq 'recurse' test.json

{
"key1": {
"key2": "value1",
"key3": "value2"
},
"foo": "bar"
}
{
"key2": "value1",
"key3": "value2"
}
"value1"
"value2"
"bar"

とまあこんな感じでオブジェクトの中身を展開してくれる。こいつを使うと、どんなに深いネストの中にあるキーで強制的に1階層目に持ってこれるわけです。代わりに余計なものがいっぱいくっついてくるけど。

なので、select関数を使って、「key2」を一階層目のキーに持たないオブジェクトを削除する。


test.json

{

"key1":{
"key2":"value1",
"key3":"value2"
},
"foo":"bar"
}


Terminal

$ jq 'recurse | select(.key2)' test.json

{
"key2": "value1",
"key3": "value2"
}

ここでの「|」は、シェルの「|」とほぼ同じ意味で、左の出力結果をそのまま右のフィルターの入力パラメータに利用する事ができる。

ここまでくればあとはフィルターをかけるだけ


Terminal

$ jq 'recurse | select(.key2) | .key2' test.json

"value1"

略式でこう書くこともできる。


Terminal

$ jq 'recurse | select(.key2).key2' test.json

"value1"

こんな感じで無事「key2」の値を取ってくることができた。

ちなみにselect関数を使わないと、以下の様な感じで「key2」を持たないオブジェクトに対するフィルター結果であるnullが残ってしまう。


test.json

{

"key1":{
"key2":"value1",
"key3":"value2"
},
"foo":"bar"
}


Terminal

$ jq 'recurse | .key2' test.json

null
"value1"


recurse応用

recurse関数は以下のように再帰的にフィルターをかけながら展開することもできる(と言うかこっちが本来の使い方?)


test.json

{

"key1":{
"key2":"value1",
"key3":"value2"
},
"foo":"bar"
}


Terminal

$ jq 'recurse(.key1)' test.json

{
"key1": {
"key2": "value1",
"key3": "value2"
},
"foo": "bar"
}
{
"key2": "value1",
"key3": "value2"
}

おんなじキー名が連続してネストされているような場合に使うらしい。そんな時あるのかな・・・

より詳しい使い方は公式サイトのマニュアル見てください。

日本語訳してくれているページも参考になりました。感謝

ブラウザでjqを試し打ちできるページもあるので、いろいろ遊ぶと楽しいです。