LoginSignup
1
0

More than 1 year has passed since last update.

Keyが揃ってないJSONデータをjqでワンライナーでヘッダ付きCSVに変換する

Posted at

業務で最近JSONをjqで弄ることが多くなったので、jqを使って遊んでたらCSVができましたという話です。

問題

元となるJSONはこんな感じです。

hoge.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}

カラムの有無もバラバラ。
これを、こんな感じにしたい。

fuga.csv
"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

くっつける

以上のテクニックを組み合わせると、ヘッダはこんな風にすると取れます。

header.sh
$ cat hoge.json | jq -s -r 'add | keys'
[
  "col_bl1",
  "col_bl2",
  "col_chr1",
  "col_chr2",
  "col_num"
]

さっきも出てきましたね。
そして明細はこんな感じ。

detail.sh
$ 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 ってしてあげるだけです。

csv.sh
$ 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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0