Berikut tocsv
dan fromcsv
fungsi memberikan solusi untuk masalah yang disebutkan kecuali satu komplikasi mengenai persyaratan (6) tentang header. Pada dasarnya, persyaratan ini dapat dipenuhi menggunakan fungsi yang diberikan di sini dengan menambahkan langkah transposisi matriks.
Apakah langkah transposisi ditambahkan atau tidak, keuntungan dari pendekatan yang diambil di sini adalah tidak ada batasan pada kunci atau nilai JSON. Secara khusus, mereka mungkin berisi titik (titik), baris baru dan/atau karakter NUL.
Dalam contoh, array objek diberikan, tetapi sebenarnya aliran dokumen JSON yang valid dapat digunakan sebagai input ke tocsv
; berkat keajaiban jq, aliran asli akan dibuat ulang oleh fromcsv
(dalam arti kesetaraan entitas demi entitas).
Tentu saja, karena tidak ada standar CSV, CSV yang dihasilkan oleh tocsv
fungsi mungkin tidak dipahami oleh semua prosesor CSV. Khususnya, harap perhatikan bahwa tocsv
fungsi yang didefinisikan di sini memetakan baris baru dalam string JSON atau nama kunci ke string dua karakter "\n" (yaitu, garis miring terbalik literal diikuti oleh huruf "n"); operasi kebalikan melakukan terjemahan terbalik untuk memenuhi "pulang pergi" persyaratan.
(Penggunaan tail
hanya untuk menyederhanakan presentasi; akan sulit untuk memodifikasi solusi untuk menjadikannya satu-satunya-jq.)
CSV dihasilkan dengan asumsi bahwa nilai apa pun dapat dimasukkan dalam bidang selama (a) bidang dikutip, dan (b) tanda kutip ganda di dalam bidang digandakan.
Solusi umum apa pun yang mendukung "pulang pergi" pasti akan menjadi agak rumit. Alasan utama mengapa solusi yang disajikan di sini lebih kompleks daripada yang diperkirakan adalah karena kolom ketiga ditambahkan, sebagian untuk memudahkan membedakan antara bilangan bulat dan string bernilai bilangan bulat, tetapi terutama karena memudahkan untuk membedakan antara ukuran-1 dan ukuran -2 array yang dihasilkan oleh jq's--stream
pilihan. Tak perlu dikatakan, ada cara lain untuk mengatasi masalah ini; jumlah panggilan ke jq juga bisa dikurangi.
Solusinya disajikan sebagai skrip pengujian yang memeriksa persyaratan bolak-balik pada kasus uji yang jitu:
#!/bin/bash
function json {
cat<<EOF
[
{
"a": 1,
"b": [
1,
2,
"1"
],
"c": "d\",ef",
"embed\"ed": "quote",
"null": null,
"string": "null",
"control characters": "a\u0000c",
"newline": "a\nb"
},
{
"x": 1
}
]
EOF
}
function tocsv {
jq -ncr --stream '
(["path", "value", "stringp"],
(inputs | . + [.[1]|type=="string"]))
| map( tostring|gsub("\"";"\"\"") | gsub("\n"; "\\n"))
| "\"\(.[0])\",\"\(.[1])\",\(.[2])"
'
}
function fromcsv {
tail -n +2 | # first duplicate backslashes and deduplicate double-quotes
jq -rR '"[\(gsub("\\\\";"\\\\") | gsub("\"\"";"\\\"") ) ]"' |
jq -c '.[2] as $s
| .[0] |= fromjson
| .[1] |= if $s then . else fromjson end
| if $s == null then [.[0]] else .[:-1] end
# handle newlines
| map(if type == "string" then gsub("\\\\n";"\n") else . end)' |
jq -n 'fromstream(inputs)'
}
# Check the roundtrip:
json | tocsv | fromcsv | jq -s '.[0] == .[1]' - <(json)
Berikut adalah CSV yang akan dihasilkan oleh json | tocsv
, kecuali bahwa SO tampaknya melarang NUL literal, jadi saya telah menggantinya dengan \0
:
"path","value",stringp
"[0,""a""]","1",false
"[0,""b"",0]","1",false
"[0,""b"",1]","2",false
"[0,""b"",2]","1",true
"[0,""b"",2]","false",null
"[0,""c""]","d"",ef",true
"[0,""embed\""ed""]","quote",true
"[0,""null""]","null",false
"[0,""string""]","null",true
"[0,""control characters""]","a\0c",true
"[0,""newline""]","a\nb",true
"[0,""newline""]","false",null
"[1,""x""]","1",false
"[1,""x""]","false",null
"[1]","false",null