読者です 読者をやめる 読者になる 読者になる

理系学生日記

おまえはいつまで学生気分なのか

忍者TOOLS

JSON を整形とか加工するのにブラウザとかエディタとか使うんだったら jq 使った方が捗る話 (基本編)

technology

brew install jq しましょう。まずはそれからだ。

JSON の整形

良くあるけど、どっかの API から雑なかんじで適当にインデントされた JSON が返却されたりして、JSONPath を書くにしろ、まずは整形しないと JSON の構造が分からん、みたいな悲しいことになって人類が不幸になる。

たとえばこんな JSON があるとして、見た瞬間ウッてなって開発者が一人不幸になる。

$ cat sample.json
{"menu": {
  "id": "file",  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}

でも、jq があればもう安心、sample.json の整形がコマンドラインからできます。

$ jq . sample.json
{
  "menu": {
    "popup": {
      "menuitem": [
        {
          "onclick": "CreateNewDoc()",
          "value": "New"
        },
        {
          "onclick": "OpenDoc()",
          "value": "Open"
        },
        {
          "onclick": "CloseDoc()",
          "value": "Close"
        }
      ]
    },
    "value": "File",
    "id": "file"
  }
}

こんな pretty-print された JSON なんて冗長すぎてクソつまらん、おれは効率重視だ、みたいな多様な要望を抱える人間のために、-c (--compact-output) なんてのもあってバイト数が良い感じになる。個人的にはウッてなるけど、多様な価値観を認めるのが人間社会の美徳です。

$ jq -c . sample.json
{"menu":{"popup":{"menuitem":[{"onclick":"CreateNewDoc()","value":"New"},{"onclick":"OpenDoc()","value":"Open"},{"onclick":"CloseDoc()","value":"Close"}]},"value":"File","id":"file"}}

フィルタ

で、ここからがフィルタ。
jq ではフィルタを書くことができて、XPath みたいな感じで特定要素を取得できたりする。たぶん、説明するよりか見た方がはやい。

$ jq ".menu.popup.menuitem" sample.json

  {
    "onclick": "CreateNewDoc()",
    "value": "New"
  },
  {
    "onclick": "OpenDoc()",
    "value": "Open"
  },
  {
    "onclick": "CloseDoc()",
    "value": "Close"
  }
]

このように、ルートから階層を辿るようにフィルタを書けば、その部分の要素が出力できる。いいかんじですね。

配列要素の一部のみ抽出

上記の要素は配列ですけど、2 番目と 3 番目の要素を抽出しようと思ったらこんなかんじ。

$ jq ".menu.popup.menuitem[1,2]" sample.json
{
  "onclick": "OpenDoc()",
  "value": "Open"
}
{
  "onclick": "CloseDoc()",
  "value": "Close"
}
配列の全要素を抽出

[1,2,3] とか全要素について書いていくのダルいから、いっそのこと何も書かなかったら全要素が抽出されて幸せが芽生える。

$ jq ".menu.popup.menuitem[]" sample.json
{
  "onclick": "CreateNewDoc()",
  "value": "New"
}
{
  "onclick": "OpenDoc()",
  "value": "Open"
}
{
  "onclick": "CloseDoc()",
  "value": "Close"
}
フィルタの組み合わせ

フィルタをパイプでつなげるみたいなこともできます。
1 プロセスでできるから「シェルのパイプは複数プロセス立ち上げるからリソースが云々」みたいなことを言うリソースお化けもイチコロだ。

$ jq ".menu.popup.menuitem[] | .onclick" sample.json
"CreateNewDoc()"
"OpenDoc()"
"CloseDoc()"


こっちのフィルタとこっちのフィルタ、両方とも通して、出力はマージしたいみたいなときあるけど、奥さん、それも jq でできます。

$ jq ".menu.popup.menuitem[] | .value, .onclick " sample.json
"New"
"CreateNewDoc()"
"Open"
"OpenDoc()"
"Close"
"CloseDoc()"

いろいろあるけど、応用編に続きます。