2021.05.06 コラム
【テックコラム】 JSON に困ったときの特効薬 jq の紹介
●はじめに
データを扱う上で良く用いられるファイルフォーマットは CSV ですね。
CSVはカラム名と行列で構成される表形式データです。
業務で SQL を使用している人は、データが表形式で表現されている方が把握しやすいですね。
片や API などから取得できるデータ形式は JSON が一般的です。
JSON は Array、Hash が組み合わされた構造化データです。
そのままで Excel で読み込めることは殆ど無く、JSON は扱いにくいイメージを持たれている方が多いのではないでしょうか。
そのギャップを埋めるツールとして、jq を紹介します。
https://stedolan.github.io/jq/
本記事の対象バージョンは 1.6 です。
ここでは JSON から CSV や TSV といった表形式データを得る視点で、基本的な操作を解説して行きます。
※以下の操作は linux と Mac、Windows は PowerShell を想定しています
※jq のインストールやコマンドラインの使い方については割愛します
● jq の役割
jq では下記の要素を組み合わせることで、値の抽出、加工、出力を行います。
- 取得する値を特定する(Identity)
- 演算や関数を適用する(フィルタ)
- array に整形し @csv や @tsv で出力する
ここからは、それぞれのステップについて解説していきます。
1.取得する値を特定する(Identity)
JSON のどの値を参照するかを示します。 JSON では Array の場合はインデックス値、Hash の場合はキー名を使い、値を取得しますよね 。
jq では、その指定に Identity を使用します。
Hash Identity
.[<string>]
hash の key が <string> の値を参照します。 .<string> と省略できます。
記述例: .["foo"] または .foo
echo '{"abc":123,"def":456}' | jq '.def' 456
Array Identity
.[<n>]
array のインデックスが <n> の要素を参照します。0 が要素の1番目になります。
インデックス値が単独の場合 .<n> と省略可能です。
記述例: .[2] または .2
echo '[10,8,6,4,2]' | jq '.[1]' 8
インデックス値は : を用いて開始と終了の範囲を指定できます。終了のインデックス値は含みません。
記述例:.[3:5] (インデックス 3 から 5 を取得)
echo '[10,8,6,4,2]' | jq '.[1:3]' [ 8, 6 ]
また、マイナス値で後方からの番号を指定できます。
記述例: .[-1] (末尾の1番目、-0 ではないことに注意)
2. 演算や関数を適用する(フィルタ)
数値の四則演算や、文字列の加工、変換などを行います。
四則演算
echo '[10,8,6,4,2]' | jq '.[1] * 10' 80
strftime: unixtime を UTC の指定書式に変換
echo '1619148896' | jq 'strftime("%Y-%m-%d %H:%M:%S")' "2021-04-23 03:34:56"
strflocaltime: unixtime を 実行環境のタイムゾーンの指定書式に変換
echo '1619148896' | jq 'strflocaltime("%Y-%m-%d %H:%M:%S %Z")' "2021-04-23 12:34:56 JST"
tostring: 数値から文字列へ変換
echo '[123, 456]' | jq '(.[0] | tostring)' "124"
gsub: 正規表現にマッチしたものを置換する
echo '["abc", "asd"]' | jq '(.[0] | sub("^ab"; "qw"))' "qwc"
上のように、Identityと組み合わせて使用する場合 | (パイプ)を使用します。
| は連続して使用することが出来ます。
また演算の範囲を括弧でくくります。
echo '[123, 456]' | jq '(.[0] | tostring), .[1]' "123" 456
3. array に整形し @csv や @tsv で出力する
jq で csv や tsv で出力するには、事前に array を作成しておく必要があります。
Array を作成するには、フィルタを [] で括りカンマで列挙します。
また jq は標準では CSV 出力時にダブルクオーテーションを \ でエスケープします。これは不要な場合が多いので、-r オプションを付与しこれを抑制します。
フィルタの最後にフォーマットを指定します。
@csv でカンマ区切りの CSV、@tsv でタブ区切りの TSV です。
echo '[1619148896]' | jq -r '.[0] | [., strftime("%Y-%m-%d")] | @csv' 1619148896,"2021-04-23"
では次に、少し複雑な JSON を TSV に変換してみましょう。
AWS の CloudWatch Logs のログは、下記の message のように awc cli で取得すると、ログ部分が JSON 形式で且つ文字列に格納されていることがあります。(JSON 内 JSON)
{ "events": [ { "logStreamName": "app-firelens-14c51c7faa05cf73fc26c6f2d", "timestamp": 1619148896812, "message": "{\"container_id\":\"701e007072e942c68c8af6e\",\"container_name\":\"/ecs-app-task-def-24-app-e0e7fc0\",\"ecs_cluster\":\"arn:aws:ecs:ap-northeast-1:33930:cluster/app-cluster\",\"ecs_task_arn\":\"arn:aws:ecs:ap-northeast-1:38730:task/app-cluster/14caa05cf73fc26c6f2d\",\"ecs_task_definition\":\"dc-tag-app-task-def:24\",\"log\":\"[INFO] cron.update_data: download data file\",\"source\":\"stderr\"}", "ingestionTime": 1619148896812, "eventId": "360308328973451422123294722" } ] }
この JSON から、
ログ日時(UTC), message 内の コンテナID, log
をコンソールでざっくりと確認することを想定します。
- コンソールで見やすくするため、CSV ではなく TSV で出力します。
- コンテナIDは元のままでは長すぎるため、先頭 6 文字のみ出力します。
- timestamp の値はミリ秒なので、秒へ変換し strftime で可読できるように変換します。
- message 内から container_id や log を取得するには、元の値を fromjson フィルタにより json として評価します。
(.message | fromjson | .container_id, .log)
- これらを [] で括り、@tsv フィルタを使用します。
コマンドラインと出力結果は以下のようになります。
cat sample_awslog.json | jq -r '.events[] | [(.timestamp / 1000 | strftime("%Y-%m-%d %H:%M:%S")), (."message" | fromjson | .container_id[0:6], .log)] | @tsv' 2021-04-23 03:34:56 701e00 [INFO] cron.update_data: download data file
これで見やすくなりましたね。
●最後に
弊社では、これまで様々な API にてデータの取得や連携を行ってまいりました。「API を利用した自動化に興味がある」や「いま使っているシステムに API は用意されているが活用できていない」といったお悩みの支援も可能ですので、お気軽にお問い合わせください。
本データに関するお問い合わせは下記にて承ります。
株式会社DataCurrent
info@datacurrent.co.jp