業務で最近JSONをjqで弄ることが多くなったので、jqを使って遊んでたらCSVができましたという話です。
問題
元となるJSONはこんな感じです。
{"col_chr1": "hoge", "col_num": 23, "col_bl1": false}
{"col_num": 24, "col_bl1": true, "col_bl2": true, "col_chr2": "nya-n"}
{"col_chr1": "hoge", "col_num": 2, "col_bl2": false}
カラムの有無もバラバラ。
これを、こんな感じにしたい。
"col_bl1","col_bl2","col_chr1","col_chr2","col_num"
false,,"hoge",,23
true,true,,"nya-n",24
,false,"hoge",,2
カラムの有無を揃えて、ヘッダも付けてCSVに。
さぁ、どうしましょうか。
答え
cat hoge.json | jq -s -r '(add | keys), ((add | keys | map({(.):null}) | add )+.[] | [.[]]) | @csv' >fuga.csv
おまじないみたいですね。
解説
add
このjqではaddを多用しています。
addは、オブジェクトの配列を引数に取るときは、各要素をマージしてくれます。
つまり、新しいkeyはオブジェクト追加、同じkeyは後のオブジェクトで上書きということですね。
これを使って、ヘッダ項目を取得しています。
$ cat hoge.json | jq -s -r 'add'
{
"col_chr1": "hoge",
"col_num": 2,
"col_bl1": true,
"col_bl2": false,
"col_chr2": "nya-n"
}
$ cat hoge.json | jq -s -r 'add | keys'
[
"col_bl1",
"col_bl2",
"col_chr1",
"col_chr2",
"col_num"
]
map({(.):null})
各keyに対して、valueがnullのオブジェクトを作ってあげます。
$ cat hoge.json | jq -s -r 'add | keys | map({(.):null}) | add'
{
"col_bl1": null,
"col_bl2": null,
"col_chr1": null,
"col_chr2": null,
"col_num": null
}
これが何の役に立つのかというと、元のJSONと + してあげることで、マージを使って足りないカラムを補完してくれるのです。
$ cat hoge.json | jq -s -r 'add | keys | map({(.):null}) | add + {"col_chr1": "hoge"}'
{
"col_bl1": null,
"col_bl2": null,
"col_chr1": "hoge",
"col_chr2": null,
"col_num": null
}
オブジェクトへの .[]
普段jqを使っていると .[]
は配列に使って、各要素にそれぞれフィルタをかけるために使うわけですが、オブジェクトに対して.[]
を適用すると、なんとvalueの要素を並べてくれます。
$ cat hoge.json | jq -s -r 'add | keys | map({(.):null}) | add + {"col_chr1": "hoge"} | .[]'
null
null
hoge
null
null
くっつける
以上のテクニックを組み合わせると、ヘッダはこんな風にすると取れます。
$ cat hoge.json | jq -s -r 'add | keys'
[
"col_bl1",
"col_bl2",
"col_chr1",
"col_chr2",
"col_num"
]
さっきも出てきましたね。
そして明細はこんな感じ。
$ cat hoge.json | jq -s -r '(add | keys | map({(.):null}) | add )+.[] | [.[]]'
[
false,
null,
"hoge",
null,
23
]
[
true,
true,
null,
"nya-n",
24
]
[
null,
false,
"hoge",
null,
2
]
あとはくっつけて @csv
ってしてあげるだけです。
$ cat hoge.json | jq -s -r '(add | keys), ((add | keys | map({(.):null}) | add )+.[] | [.[]]) | @csv'
"col_bl1","col_bl2","col_chr1","col_chr2","col_num"
false,,"hoge",,23
true,true,,"nya-n",24
,false,"hoge",,2
以上!
参考
https://dev.classmethod.jp/articles/jq-manual-japanese-translation-roughly/
https://cloudpack.media/51306
https://medium.com/veltra-engineering/jq-supports-json-to-csv-fb5c951a9575