Help us understand the problem. What is going on with this article?

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を試し打ちできるページもあるので、いろいろ遊ぶと楽しいです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away