W jaki sposób za pomocą jq można przekonwertować dowolne kodowanie JSON tablicy płytkich obiektów na format CSV?
W tej witrynie jest wiele pytań i odpowiedzi, które obejmują określone modele danych, które trwale kodują pola, ale odpowiedzi na to pytanie powinny działać dla dowolnego formatu JSON, z jedynym ograniczeniem, że jest to tablica obiektów o właściwościach skalarnych (bez głębokich / złożonych / podobiekty, ponieważ spłaszczanie to inna kwestia). Wynik powinien zawierać wiersz nagłówka z nazwami pól. Preferowane będą odpowiedzi, które zachowują kolejność pól pierwszego obiektu, ale nie jest to wymagane. Wyniki mogą obejmować wszystkie komórki w cudzysłów lub tylko te, które wymagają cudzysłowu (np. „A, b”).
Przykłady
Wejście:
[ {"code": "NSW", "name": "New South Wales", "level":"state", "country": "AU"}, {"code": "AB", "name": "Alberta", "level":"province", "country": "CA"}, {"code": "ABD", "name": "Aberdeenshire", "level":"council area", "country": "GB"}, {"code": "AK", "name": "Alaska", "level":"state", "country": "US"} ]
Możliwe wyjście:
code,name,level,country NSW,New South Wales,state,AU AB,Alberta,province,CA ABD,Aberdeenshire,council area,GB AK,Alaska,state,US
Możliwe wyjście:
"code","name","level","country" "NSW","New South Wales","state","AU" "AB","Alberta","province","CA" "ABD","Aberdeenshire","council area","GB" "AK","Alaska","state","US"
Wejście:
[ {"name": "bang", "value": "!", "level": 0}, {"name": "letters", "value": "a,b,c", "level": 0}, {"name": "letters", "value": "x,y,z", "level": 1}, {"name": "bang", "value": "\"!\"", "level": 1} ]
Możliwe wyjście:
name,value,level bang,!,0 letters,"a,b,c",0 letters,"x,y,z",1 bang,"""!""",0
Możliwe wyjście:
"name","value","level" "bang","!","0" "letters","a,b,c","0" "letters","x,y,z","1" "bang","""!""","1"
json2csv
jest na stackoverflow.com/questions/57242240/ ...Odpowiedzi:
Najpierw uzyskaj tablicę zawierającą wszystkie różne nazwy właściwości obiektów w danych wejściowych tablicy obiektów. To będą kolumny Twojego pliku CSV:
Następnie dla każdego obiektu w danych wejściowych tablicy obiektów przypisz nazwy kolumn, które uzyskasz, do odpowiednich właściwości w obiekcie. To będą rzędy Twojego pliku CSV.
Na koniec umieść nazwy kolumn przed wierszami jako nagłówek pliku CSV i przekaż wynikowy strumień wierszy do
@csv
filtra.Teraz wszyscy razem. Pamiętaj, aby użyć
-r
flagi, aby otrzymać wynik jako nieprzetworzony ciąg:źródło
$rows
przypisania zmiennej, po prostu wstawiając ją:(map(keys) | add | unique) as $cols | $cols, map(. as $row | $cols | map($row[.]))[] | @csv
$rows
nie musi być przypisane do zmiennej; Po prostu pomyślałem, że przypisanie go do zmiennej sprawiło, że wyjaśnienie było przyjemniejsze.Chudy
lub:
Szczegóły
Na bok
Opisanie szczegółów jest trudne, ponieważ jq jest zorientowane strumieniowo, co oznacza, że działa na sekwencji danych JSON, a nie na pojedynczej wartości. Strumień wejściowy JSON jest konwertowany na jakiś typ wewnętrzny, który jest przepuszczany przez filtry, a następnie kodowany w strumieniu wyjściowym na końcu programu. Typ wewnętrzny nie jest modelowany przez JSON i nie istnieje jako nazwany typ. Najłatwiej to wykazać, badając dane wyjściowe samego indeksu (
.[]
) lub operatora przecinka (zbadanie tego bezpośrednio można wykonać za pomocą debugera, ale byłoby to pod względem wewnętrznych typów danych jq, a nie pojęciowych typów danych za JSON) .Zauważ, że dane wyjściowe nie są tablicą (która byłaby
["a", "b"]
). Kompaktowe dane wyjściowe (-c
opcja) pokazują, że każdy element tablicy (lub argument,
filtru) staje się oddzielnym obiektem w wyniku (każdy znajduje się w osobnym wierszu).Strumień jest podobny do sekwencji JSON , ale używa znaków nowej linii zamiast RS jako separatora danych wyjściowych po zakodowaniu. W konsekwencji do tego typu wewnętrznego w tej odpowiedzi odnosi się ogólny termin „sekwencja”, przy czym „strumień” jest zarezerwowany dla zakodowanego wejścia i wyjścia.
Konstruowanie filtra
Klucze pierwszego obiektu można wyodrębnić za pomocą:
Klucze będą zazwyczaj przechowywane w oryginalnej kolejności, ale zachowanie dokładnej kolejności nie jest gwarantowane. W związku z tym będą musiały zostać użyte do indeksowania obiektów, aby uzyskać wartości w tej samej kolejności. Zapobiegnie to również umieszczaniu wartości w niewłaściwych kolumnach, jeśli niektóre obiekty mają inną kolejność kluczy.
Aby oba wyprowadzić klucze jako pierwszy wiersz i udostępnić je do indeksowania, są one przechowywane w zmiennej. Następny etap potoku odwołuje się następnie do tej zmiennej i używa operatora przecinka, aby dołączyć nagłówek do strumienia wyjściowego.
Wyrażenie po przecinku jest trochę zawiłe. Operator indeksu na obiekcie może przyjąć sekwencję ciągów (np.
"name", "value"
), Zwracając sekwencję wartości właściwości dla tych ciągów.$keys
jest tablicą, a nie sekwencją, więc[]
jest stosowana do konwersji na sekwencję,które można następnie przekazać
.[]
To także tworzy sekwencję, więc konstruktor tablicy jest używany do konwersji jej na tablicę.
To wyrażenie ma być zastosowane do pojedynczego obiektu.
map()
służy do zastosowania go do wszystkich obiektów w tablicy zewnętrznej:Na koniec na tym etapie jest to konwertowane na sekwencję, dzięki czemu każdy element staje się oddzielnym wierszem w wyniku.
Po co pakować sekwencję w tablicę
map
tylko po to, aby ją uwolnić?map
tworzy tablicę;.[ $keys[] ]
tworzy sekwencję. Zastosowaniemap
do sekwencji z.[ $keys[] ]
utworzy tablicę sekwencji wartości, ale ponieważ sekwencje nie są typu JSON, więc zamiast tego otrzymasz spłaszczoną tablicę zawierającą wszystkie wartości.Wartości z każdego obiektu należy przechowywać oddzielnie, aby w ostatecznym wyniku stały się oddzielnymi wierszami.
Na koniec sekwencja jest przekazywana przez
@csv
formater.Alternatywny
Elementy można oddzielić raczej późno niż wcześnie. Zamiast używać operatora przecinka, aby uzyskać sekwencję (przekazując sekwencję jako prawy operand), sekwencja nagłówka (
$keys
) może zostać umieszczona w tablicy i+
użyta do dołączenia tablicy wartości. To nadal musi zostać przekonwertowane na sekwencję przed przekazaniem do@csv
.źródło
keys_unsorted
zamiast,keys
aby zachować kolejność kluczy od pierwszego obiektu?[{"a":1,"b":2,"c":3}]
.Stworzyłem funkcję, która wyprowadza tablicę obiektów lub tablic do csv z nagłówkami. Kolumny byłyby w kolejności nagłówków.
Więc możesz go użyć w ten sposób:
źródło
Poniższy filtr jest nieco inny, ponieważ zapewnia konwersję każdej wartości na ciąg. (Uwaga: użyj jq 1.5+)
Filtr:
filter.jq
źródło
unique
są sortowane, więcunique|sort
można je uprościć dounique
.-r
opcji. W przeciwnym razie wszystkie cudzysłowy"
zostaną zastąpione dodatkowymi znakami ucieczki, co nie jest prawidłowym plikiem CSV.Ten wariant programu Santiago jest również bezpieczny, ale zapewnia, że nazwy kluczy w pierwszym obiekcie są używane jako nagłówki pierwszej kolumny, w tej samej kolejności, w jakiej pojawiają się w tym obiekcie:
źródło