JSON処理のコマンドラインツールであるjqは、JSONテキストを1行でちゃくっと解析、変換するときに使うものです。単体では複雑なことは得手ではないので、そうしたときはsedやawkなど他のユーティリティを併用するのが通例です。
とは言え、jqにも変数定義などプログラミング言語らしき機能が備わっているので、「やればできんじゃねぇ?」と野望を募らせてしまうこともあります。
ここでは、JSONオブジェクト値の表形式への整理を試みます。具体的には、次のような同一形式のオブジェクトの配列を受け取り、
[
{
"name": "Penfolds Bin 389",
"region": "South Austraria"
},
{
"name": "Chateau Cantenac Brown",
"region": "Margaux"
},
{
"name": "Tenuta San Guido Le Difese",
"region": "Bolgheri"
}
]
オブジェクトの値で次のような表形式で出力します。
Penfolds Bin 389 South Austraria
Chateau Cantenac Brown Margaux
Tenuta San Guido Le Difese Bolgheri
以下、入力データはファイルtabular-sample.jsonに収容されているとします。
コード
コード(フィルタファイル)はtabular.jqで、次のGithubから取得できます(短いのでコピペでも十分ですが)。
https://github.com/stoyosawa/jqDoc-public/
フィルタファイルの改行はUnixスタイルのLFだけでなければならないので、保存時に注意してください。Windows流にCRLFだと次のエラーが報告されます。
jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
at <top-level>, line 1:
フィルタファイルの使い方
これらファイルをjqから呼び出すときは、次のオプションを指定します。
-
-fオプション(--from-file)ーフィルタファイルを読み込みます。 -
-rオプション(--raw-output)ー文字列を囲む二重引用符(")を外します。
まとめると、次のようになります。
$ jq -rf tabular.jq tabular-sample.json
タブ区切り
オブジェクトの値を配列化し、それをタブ記号(\t)で連結するというのが、最も簡単な手でしょう。
$ cat tabular-sample.json | jq -r '.[] | [.name, .region] | join("\t")'
JSON全体を囲む[]の要素でループ(上記.[])し、それぞれについてnameとregionの値を要素とした配列を生成し([.name, .region])、それら要素をjoin()で連結するわけです。結果は次のようになります。
Penfolds Bin 389 South Austraria
Chateau Cantenac Brown Margaux
Tenuta San Guido Le Difese Bolgheri
おおむねできました。しかし、左側(name)の文字列の長さに幅があるので、タブでは2列目がうまく揃いません。
スペースでパディング
こういうときは文字列をスペースでパディングし、あとから同じ長さで切り揃えます。強引ですが、sprintf()のないJavaScriptでよくやる手です。
ここでの文字列は最長で26文字なので、30文字のスペースを右側に加え(+ " "*30)て右パディングした上で、スライス([:30])で左から30文字を切り取ります。
$ cat tabular-sample.json | jq -r '.[] | [(.name + " "*30)[:30], .region] | join("")'
結果は次の通りです。
Penfolds Bin 389 South Austraria
Chateau Cantenac Brown Margaux
Tenuta San Guido Le Difese Bolgheri
最長文字列に合わせる
これだと、最初に最長文字を確認しなければならないので面倒です。そこで、事前に最長文字列をlengthとmaxから調べ、変数$namelenに収容します。
フィルタは次の通りです。
. |
( # 変数に最大長+5を代入
[
.[] |
.name | # それぞれの name の
length # 長さ
] |
max + 5 # 最大値+5
) as $namelen | # $namelen に代入
.[] | # あとは同じ
[
(.name + " " * $namelen)[:$namelen],
.region
] |
join("")
最初の.|で、入力JSONテキストをそのまま次段(2~9行目)の最大長の計算にパイプします。() as $namelenは代入式なので、その先(10行目)の.に影響は与えません。
まず、入力全体を.[]でループし、逐次.name | lengthで文字列長を取得します。このループは[](3行目と7行目)で括られているので、生成されるのはnameの文字列長の配列です。これをmax(8行目)に通すと、最大値が得られます。ここでは1列目と2列目の間に5つのスペースを加えるために、追加で+5しています。結果はas $namelenで変数に代入します。
以降は、直値の代わりに変数を用いている以外、これまでと変わりません。
実行例を示します。
$ cat tabular-sample.json | jq -rf tabular.jq
Penfolds Bin 389 South Austraria
Chateau Cantenac Brown Margaux
Tenuta San Guido Le Difese Bolgheri
おわりに
ええ、実用性はゼロです。Unixユーザならcolumnを使いますよね。
$ jq -r '.[] | [ .[] ] | join("|")' tabular-sample.json | column -t -s "|"
Penfolds Bin 389 South Austraria
Chateau Cantenac Brown Margaux
Tenuta San Guido Le Difese Bolgheri
参考
-
./jq -
jqのオフィシャルサイトです(英文)。 -
man page of COLUMN - Unixコマンドの
columnのman pageです。 -
jqマニュアル(開発バージョン) - Yuji Okazawa氏が(非公式に)訳しておられる
jqマニュアルの日本語版です。和訳マニュアルは他にもあるので、検索してください。 - The JavaScript Object Notation (JSON) Data Interchange Format(RFC 8259) - JSONの仕様です(英文)。しばしばRFC 7159が参照されますが、そちらはobsoleteになりました。
- JSON(JavaScript Object Notation)データ交換フォーマット - 上記の非公式の和訳です。
- jqハンドブックーNetOps/DevOps必携のJSONパーザ - 表形式出力はこちらでも取り上げています。【出版社 | honto | amazon.co.jp | ヨドバシカメラ 】。
