jq と JMESPath を同時に覚える

はじめに

ご無沙汰しております。田中です。今回は jq と JMESpath について書きます。

AWS CLI の返り値の JSON は、以下のように --query オプションに JMESPath を指定して処理できます。

ですが、JMESPath は jq の構文と似ていて混乱するし、わざわざ覚えるのも面倒なので、jq コマンドさえ覚えておけばそれでいい、と思ってしまいます。

しかし、以前に、作業中のサーバに jq がインストールされていないくて困る、ということがありました。そもそも JMESPath で指定できるのに、わざわざ jq を使うというのも何か負けた気がするし、そういえば jq 自体もきちんと理解して使っているわけではなかったので、この際、両者を同時に正しく覚えておこうと思いました。

尚、以下の jq / JMESPath は、それぞれの公式ページの playground で確認しました。

  • jq  (playground は "Try online!" から)
  • JMESPath

 

現在の値をそのまま返す

. / @ は、| や関数に値を渡すときに、現在処理している値を表します。

配列/オブジェクトの要素を取得

jq では . が必要です。JMESPath では @[1], @.y とする必要はありません。

ネストした配列/オブジェクトから値を取得する場合は、以下のようになります。

取得した値を使用して新たに配列/オブジェクトを作成することもできます。

JMESPath の引用符が、少し複雑です。JMESPath では、JSON として評価される値を直接書く場合、` (back quote) で囲む必要があります。よって、数値は `30` のように書く必要があります。文字列は `"Z"` のようになりますが (JSON の文字列は " (double quote) で囲むので) 、 これとは別に文字列リテラルを表す ' (single quote) があり、'Z' と書くこともできます。
(ただ、`Z` と書いても動作します。これは、どう解釈すればいいでしょう......)
 

配列の map 処理

● jq の [] と JMESPath の [*]

jq の [] は iterator 、JMESPath の [*] は projection と呼ばれ、JavaScript で言えば map 関数のようなことを行います。どちらも独特の動作をし、混乱しがちなので、少し詳しく説明します。
 

● jq の iterator

jq の [] は、配列を要素の行に分解します。上記の一番目の例では、以下の順で処理がされています。

iterator の処理結果は複数の JSON の行であり、配列ではないので、配列と考えて要素を取得しようとするとエラーになります。

 

● JMESPath の projection

JMESPath の [*] は、jq の iterator のように行に分解しているわけではありませんが、projection の 結果から要素を取得するときは、やはり、注意が必要です。

[*].x の結果は、やはり projection なので、これに対し [1] を指定すると、各要素である数値 0, 1, 2 に対してインデックスを指定していることになります。配列でないものにインデックスを指定した場合は null が返り、また projection において null は無視される仕様(後述)なので、結果として空配列が返ることになります。よって、projection の終了を示すために、間に | を挟む必要があるというわけです。

● オブジェクトに対する iterator / projection

オブジェクトに対しても、 iterator / projection を使用できます。この場合、オブジェクトの値の配列に対して処理されます。

jq では配列の場合と全く同じ [] ですが、JMESPath では [*] ではなく * を使用します。
 

● null の処理

iterator / projection では、null の処理に違いがあります。

JMESPath の projection では null が無視されます。jq と同じ結果にしたい場合は、後述の map 関数を使いましょう。
また、jq の iterator の処理の結果から null を除去したい場合は、null でない値のみを選択する関数 values が使えます。

 

● map 関数

iterator / projection と同様の処理をするのに、map 関数を使うこともできます。
jq では、現在値を処理対象にするので、引数は処理内容のみです。JMESPath では、処理対象の配列(上例では現在値を表す @)も引数に指定します。

&x の & は「x の値ではなく x という JMESPath の構文だ」ということを表すために必要ということみたいですが...... まあ、こういう書き方をすると覚えておきましょう。

尚、JMESPath の map は projection ではないので、結果も配列になります。

他の関数と組み合わせて使用することもできます。

JMESPath の例において、右の @ は map から見た現在値(["0", 1, "2"])で、左の @ は to_number から見た現在値("0" 又は "1" 又は "2")です。
 

絞り込み

jq には特別な構文はないので、select 関数を使用します。
JMESPath では [? 真偽値を返す式] という構文を使います。これも projection です。[*] の * を条件式と入れ替えたもの、と覚えましょう。

ソート

JMESPath の sort_by の引数の位置、map と逆ですね。何故でしょう......
 

flatten

JMESPath の [] は、これも projection です。[*] の * がなくなったもの、と覚えましょう。jq の iterator と混同しないように注意して下さい。任意の深さまで全て flatten する方法はないようなので、深さが分からないときは [][][][][][][][][][] ぐらいにしておけば......
 

スライス

スライスの使い方は、jq と JMESPath で殆ど同じですが、増分値を指定できるのは JMESPath のみです。JMESPath のスライスは、これも projection です。
 

join

 

最後に

どちらの構文も一長一短な気がします。ただ、jq の方が機能が多いです。JMESPath では、例えば以下のことができないようです。
※ 筆者の理解不足かもしれません。方法をご存じの方は、ご教授下さい。

とにかく、ここに書いたことぐらいを覚えておけば、一番始めに挙げた AWS CLI の例ぐらいのことはできるので、とりあえず十分ではないでしょうか。

この記事が皆様の日々の作業の一助となれば幸いです。

尚、本記事の執筆にあたり、上掲の公式サイトを参考にさせて頂きました。
 

Comments are closed, but you can leave a trackback: Trackback URL.