tocsv
sau và fromcsv
các hàm cung cấp giải pháp cho vấn đề đã nêu ngoại trừ một phức tạp liên quan đến yêu cầu (6) liên quan đến tiêu đề. Về cơ bản, yêu cầu này có thể được đáp ứng bằng cách sử dụng các chức năng được đưa ra ở đây bằng cách thêm bước chuyển vị ma trận.
Cho dù có thêm bước chuyển vị hay không, thì ưu điểm của phương pháp được thực hiện ở đây là không có giới hạn nào đối với các khóa hoặc giá trị JSON. Đặc biệt, chúng có thể chứa dấu chấm (dấu chấm), dòng mới và / hoặc ký tự NUL.
Trong ví dụ, một mảng đối tượng được đưa ra, nhưng trên thực tế, bất kỳ luồng tài liệu JSON hợp lệ nào cũng có thể được sử dụng làm đầu vào cho tocsv
; nhờ sự kỳ diệu của jq, luồng gốc sẽ được tạo lại bởi fromcsv
(theo nghĩa bình đẳng giữa từng thực thể).
Tất nhiên, vì không có tiêu chuẩn CSV nên CSV được tạo bởi tocsv
chức năng có thể không được hiểu bởi tất cả các bộ xử lý CSV. Inparticular, xin lưu ý rằng tocsv
hàm được xác định ở đây ánh xạ các dòng mới được nhúng trong chuỗi JSON hoặc tên khóa thành chuỗi hai ký tự "\ n" (tức là dấu gạch chéo ngược theo nghĩa đen theo sau là ký tự "n"); phép toán nghịch đảo thực hiện phép dịch ngược để đáp ứng "khứ hồi" yêu cầu.
(Việc sử dụng tail
chỉ là để đơn giản hóa việc trình bày; sẽ rất tốt nếu sửa đổi giải pháp để biến nó thành một giải pháp duy nhất.)
CSV được tạo dựa trên giả định rằng bất kỳ giá trị nào cũng có thể được bao gồm trong một trường miễn là (a) trường được trích dẫn và (b) dấu ngoặc kép trong trường được nhân đôi.
Bất kỳ giải pháp chung nào hỗ trợ "các chuyến đi khứ hồi" nhất định sẽ hơi phức tạp. Lý do chính tại sao giải pháp được trình bày ở đây phức tạp hơn người ta có thể mong đợi là do cột thứ ba được thêm vào, một phần để giúp dễ dàng phân biệt giữa số nguyên và chuỗi có giá trị tích phân, nhưng chủ yếu là vì nó giúp dễ dàng phân biệt giữa kích thước-1 và kích thước -2 mảng được tạo bởi jq's --stream
quyền mua. Không cần phải nói, có những vấn đề khác có thể được giải quyết; số lượng cuộc gọi đến jq cũng có thể bị giảm.
Giải pháp được trình bày dưới dạng một kịch bản thử nghiệm để kiểm tra yêu cầu khứ hồi đối với một trường hợp thử nghiệm đang nói:
#!/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)
Đây là CSV sẽ được tạo ra bởi json | tocsv
, ngoại trừ SO dường như không cho phép NUL theo nghĩa đen, vì vậy tôi đã thay thế nó bằng \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