Følgende tocsv
og fromcsv
funktioner giver en løsning på det angivne problem bortset fra en komplikation vedrørende krav (6) vedrørende overskrifterne. I det væsentlige kan dette krav opfyldes ved at bruge de funktioner, der er angivet her, ved at tilføje et matrixtransponeringstrin.
Uanset om der tilføjes et transponeringstrin eller ej, er fordelen ved fremgangsmåden her, at der ikke er nogen begrænsninger på JSON-nøglerne eller -værdierne. De kan især indeholde punktum (prikker), nye linjer og/eller NUL-tegn.
I eksemplet er et array af objekter givet, men faktisk kan enhver strøm af gyldige JSON-dokumenter bruges som input til tocsv
; takket være magien ved jq vil den originale stream blive genskabt af fromcsv
(i betydningen enhed for enhed lighed).
Da der ikke er nogen CSV-standard, produceres CSV'en af tocsv
Funktionen kan muligvis ikke forstås af alle CSV-processorer. Bemærk især, at tocsv
funktion, der er defineret her, tilknyttede nye linjer i JSON-strenge eller nøglenavne til to-tegnsstrengen "\n" (dvs. en bogstavelig omvendt skråstreg efterfulgt af bogstavet "n"); den inverse operation udfører den omvendte oversættelse for at opfylde "rundturen" krav.
(Brugen af tail
er blot for at forenkle præsentationen; det ville være bedre at modificere løsningen for at gøre den til en kun-jq.)
CSV'en genereres ud fra den antagelse, at enhver værdi kan inkluderes i et felt, så længe (a) feltet er citeret, og (b) dobbelte anførselstegn i feltet fordobles.
Enhver generisk løsning, der understøtter "rundrejser", er bundet til at være noget kompliceret. Hovedårsagen til, at den her præsenterede løsning er mere kompleks, end man kunne forvente, er, at der er tilføjet en tredje kolonne, dels for at gøre det nemt at skelne mellem heltal og strenge med heltalværdi, men hovedsagelig fordi det gør det nemt at skelne mellem størrelsen-1 og størrelsen -2 arrays produceret af jq's--stream
mulighed. Det er overflødigt at sige, at der er andre måder, hvorpå disse problemer kan løses; antallet af opkald til jq kunne også reduceres.
Løsningen præsenteres som et testscript, der kontrollerer kravet om tur/retur på en sigende testcase:
#!/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)
Her er den CSV, der ville blive produceret af json | tocsv
, bortset fra at SO lader til at ikke tillade bogstavelige NUL'er, så jeg har erstattet det med \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