kakakakakku blog

Weekly Tech Blog: Keep on Learning!

JMESPath Tutorial と jp コマンドを使って JMESPath を学ぶ

JSON をパース(集計/整形など)するときに,よく使われているのは「jq」だと思う.僕も jq をよく使っている(構文は毎回調べるけど).今回は jq 以外の選択肢となる「JMESPath」の理解を深めることにした.JMESPath も JSON をパースできるクエリ言語(仕様)で,例えば AWS CLI の --query オプションでも採用されている.

jmespath.org

JMESPath Tutorial

JMESPath をすぐに試すなら「JMESPath Tutorial」を使うと便利.

jmespath.org

よく使う構文を画面上で試すことができるし,実際にクエリを更新するとすぐに反映される.JMESPath Tutorial で試せる構文は以下となる.

  • Basic Expressions
  • Slicing
  • Projections
    • List and Slice Projections
    • Object Projections
    • Flatten Projections
    • Filter Projections
  • Pipe Expressions
  • MultiSelect
  • Functions

f:id:kakku22:20190513003424p:plain

JMESPath CLI : jp

CLI として使える JMESPath 実装 jp コマンドもある.良くも悪くも jq と似すぎ!

github.com

今回は brew を使ってインストールしてみた.

$ brew install jmespath/jmespath/jp
$ jp --version
jp version 0.1.3

なお,現在 README.md に書いてある手順だと異なる jp が brew からインストールされてしまうため,直接 Formulae を指定する手順に修正するプルリクエストを送った.確実に必要な修正だから merge してもらえると良いんだけど!

github.com

基本的な jp の使い方として,標準出力をパイプして JSON をパースできる.

$ echo '{"key": "value"}' | jp key
"value"

$ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar[1]
"b"

既に JSON ファイルがある場合,-f もしくは --filename を使うと,ファイルを読み込んで JSON をパースできる.

$ jp --filename input.json foo.bar[1]
"b"

jq を使う場合,よく -r もしくは --raw-output を使って,パース結果に " を付けないようにすると思う.jp にも同様のオプションがあり,-u もしくは --unquoted を使う.また環境変数 JP_UNQUOTED を使うと,デフォルト設定を変更することもできる.

$ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp --unquoted foo.bar[1]
b

$ export JP_UNQUOTED=true
$ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar[1]
b

JMESPath Tutorial と jp コマンド

JMESPath Tutorial を一通り試して,気になった構文を jp コマンドを使って整理しておこうと思う.

Slicing

「Slicing」では,例えば [0:5] のように書くと,配列データの一部を刈り取ることができる.また [:5] のように終了のみを指定して刈り取ることもできる.

$ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [2:5]
[
  2,
  3,
  4
]

$ echo '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' | jp [:5]
[
  0,
  1,
  2,
  3,
  4
]

Object Projections

「Object Projections」では,例えば ops.*.numArgs のように中間要素をワイルドカードで指定することができる.

$ echo '{
  "ops": {
    "functionA": {"numArgs": 2},
    "functionB": {"numArgs": 3},
    "functionC": {"variadic": true}
  }
}' | jp ops.*.numArgs
[
  2,
  3
]

Filter Projections

「Filter Projections」では,例えば machines[?state=='running'].name のように要素に対して条件を書くことができる.

$ echo '{
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "b", "state": "running"}
  ]
}' | jp "machines[?state=='running'].name"
[
  "a",
  "b"
]

Functions

「Functions」では,例えば length(people) など,ビルトイン関数を使って集計することもできる.他にも maxmerge など,多く用意されている.

$ echo '{
  "people": [
    {
      "name": "b",
      "age": 30,
      "state": {"name": "up"}
    },
    {
      "name": "a",
      "age": 50,
      "state": {"name": "down"}
    },
    {
      "name": "c",
      "age": 40,
      "state": {"name": "up"}
    }
  ]
}' | jp "length(people)"
3

JMESPath Specification

JMESPath の全仕様は「JMESPath Specification」に載っている.ビルトイン関数の一覧も確認できる.

jmespath.org

言語別 JMESPath 実装

多くの言語に JMESPath 実装がある.バックエンド実装で JSON をパースする場面があれば検討しても良さそう.

  • Python
  • PHP
  • JavaScript
  • Ruby
  • Lua
  • Go
  • Java
  • Rust
  • .NET

jmespath.org

まとめ

  • JSON をパース(集計/整形など)するときに「jq」以外の選択肢となる「JMESPath」を試した
  • JMESPath をすぐに試すなら「JMESPath Tutorial」を使うと便利
  • JMESPath CLI として使える jp コマンドもある
  • (慣れの問題もあるけど)個人的には「jq」よりも「JMESPath」の方が構文の可読性が高いように感じた