JSON
jmespath

【個人メモ】JSONのパースにjmespathを使ってみよう

More than 3 years have passed since last update.

jmespath

Screen Shot 2015-02-15 at 19.10.37.png

JSONのパースには、jqというツールが有名である。
が、昨日、aws-cliにて、--query オプションにより
jmespath形式でのJSONパースを利用できる、ということで
jmespathそのものを試してみることにした。

jmespathをインストール

pipでインストールできる。

> pip install —user jmespath

インストール後、コマンドラインツールとしてjpコマンドを実行することが可能になる

> echo '{"a": "100"}' | jp a
“100"

簡単だ。

aws-cliでの出力を例に取りながら、
jmespathでのオプションをさらっと見ていく

JMESPath Tutorial

に書かれている。

例)aws-cliを利用してec2 instanceのprivate ipアドレスのみを取得してみる

管理している同立しているサービス向けのインスタンスの台数が30台〜とかなると
IPアドレスを抜くにも一苦労だったりする。

aws-cliでtext出力を行い、cutに食わせて出させる場合

aws ec2 describe-instances --filter "Name=tag:Role,Values=web-front" --output text | grep PRIVATEIPADDRESSES | cut -f3
10.0.1.1
10.0.1.2
10.0.1.3
10.0.1.4
10.0.1.5
...
10.0.1.50

こんな感じになってしまう。

jmespathを利用してaws-cliのJSON出力をパースするようにする

aws ec2 describe-instances --filter "Name=tag:Role,Values=web-front" --query "Reservations[*].Instances[*].PrivateIpAddress[]”

[
  “10.0.1.1”,
  “10.0.1.2”,
  “10.0.1.3”,
  “10.0.1.4”,
  “10.0.1.5”,
  …
  “10.0.1.50”
]

便利組み込み関数を利用してみる

aws cliがjmespathのクエリパスをサポートしているのは確認できた。
その他、jmespathに組み込まれているsearch関数を利用してみよう

search関数

search関数は表現力が高い。
Hashのキーマッチングやフィルタリングに対応している。

たとえば

以下のJSONデータがあるとする

[
  {
    "name": "Keiji Matsuzaki",
    "age": 33,
    "language":
    [
      "Japanese"
    ],
    "programming": {
        "language":
          [
            "PHP",
            "Python",
            "Go",
            "Ruby"
          ]
    }
  },
  {
    "name": "Fujisan Taro",
    "age": 24,
    "language":
    [
      "Japanese"
    ],
    "programming": {
        "language":
          [
            "Rust",
            "C++",
            "Erlang",
            "Go"
          ]
    }
  },
  {
    "name": "Lim Chow",
    "age": 28,
    "language":
    [
      "English",
      "Chinese"
    ],
    "programming": {
        "language":
          [
            "Ruby",
            "sass",
            "Lua",
            "C"
          ]
    }
  },
  {
    "name": "Jem Tomas",
    "age": 52,
    "language":
    [
      "English",
      "French"
    ],
    "programming": {
        "language":
          [
            "C",
            "BASIC",
            "NASM",
            "HTML",
            "Go",
            "Haskell"
          ]
    }
  }
]

ここから、以下の条件に合致するデータを取り出す

language: englishが含まれているレコードはどれぐらいあるか

jmespathでパースする場合はこうなる

jp -f test.json 'length([].language[?@==`English`][])'
> 2

どういうことか

[].language[] で、languageの要素を取得する。

[
    [
        "Japanese"
    ],
    [
        "Japanese"
    ],
    [
        "English",
        "Chinese"
    ],
    [
        "English",
        "French"
    ]
]

language[]のものから、current node == Englishに合致するものを検索する

[].language[?@==English]

[
    [],
    [],
    [
        "English"
    ],
    [
        "English"
    ]
]

arrayごとに分割されているのでflatten化する

[].language[?@==English][]

[
    "English",
    "English"
]

length関数により、要素数を数える

length([].language[?@==English][])

> 2

なお、jpコマンドはデフォルトでsearch関数を利用するようになっている。
jp#43

年齢の平均

jp -f test.json 'avg([].age)’
> 34.25

年齢が一番高い人

jp -f test.json 'max_by([], &age)’
{
    "age": 52,
    "programming": {
        "language": [
            "C",
            "BASIC",
            "NASM",
            "HTML",
            "Go",
            "Haskell"
        ]
    },
    "name": "Jem Tomas",
    "language": [
        "English",
        "French"
    ]
}

それ以外にも関数はあるけど、書いてて時間が足りないので
必要になったら使ってみよう

jmespathで利用できる関数一覧