2014-08-28 11 views
15

ho i seguenti dati JSON:Come posso convertire il mio JSON in CSV usando jq?

{"id":"111","case":"Y","custom":{"speech invoked":"no","input method":"hard","session ID":"420"}} 

Come posso convertirlo in formato CSV utilizzando jq quindi il mio risultato assomiglia a questo?

id,case,session Id,speech invoked,input method 

111,Y,420,no,hard 

Ho provato quanto segue, ma non ha funzionato:

{(.id),(.case),(.custom."session Id"),(.custom."speech invoked"),(.custom."input method")} 

Se non è possibile alcuna soluzione Perl o shell è apprezzato.

risposta

-1

Utilizzo di Perl ed il suo modulo JSON:

#!/usr/bin/perl 
use warnings; 
use strict; 
use feature qw{ say }; 

use JSON; 

my $input = << '__JSON__'; 
{"id":"111","case":"Y","custom":{"speech invoked":"no","input method":"hard","session ID":"420"}} 
__JSON__ 

my $struct = decode_json($input); 

my @header = grep ! ref $struct->{$_}, keys %$struct; 
push @header, map keys %{ $struct->{$_} }, 
       grep ref $struct->{$_}, 
       keys %$struct; 

my @row = grep ! ref, values %$struct; 
push @row, map values %$_, grep ref, values %$struct; 

say join ',', @header; 
say join ',', @row; 
+0

avrei ancora voglia di utilizzare Text :: CSV_XS per creare l'output nel caso in cui alcuni dei dati include le cose come gli spazi, le virgolette , virgole, ecc. – Tanktalus

+0

@Tanktalus: certo. Basta modificare le ultime due righe. – choroba

+1

Questo non usa 'jq' come la domanda chiede. – reinierpost

16

Utilizzando perl non era una buona soluzione per me, ma dopo un po 'di tentativi ed errori ho capito che si può fare con un semplice jq utilizzando l'operatore join() .

Prima creare un array dell'output necessario, quindi unire gli elementi dell'array usando le virgole.

jq -r '[.case, .custom."speech invoked", .custom."input method"] | join(", ")' 

Divertiti. :)

+0

Ho omesso '.custom." ID sessione "' in modo che le persone possano vedere 'join()' senza scorrere. –

+1

Grazie Joe, sarebbe utile a molti altri. – user2711819

11

Utilizzando JQ, è possibile utilizzare questo filtro:

with_entries(select(.key != "custom")) + .custom 
    | to_entries 
    | map(.key), map(.value) 
    | @csv 

Basta notare che scritto in questo modo, le proprietà di "custom" saranno sempre scritti alla fine, non importa in che ordine le proprietà sono in .

+1

È bello perché ottiene automaticamente le chiavi senza conoscerle in anticipo e emette le intestazioni delle colonne. Ma il problema è che le intestazioni sono * ripetute * per ogni riga. Se hai un input variabile puoi tranquillamente emettere solo i valori ma se l'input è variabile avrai bisogno delle intestazioni. Stash l'input e l'output delle intestazioni solo nel file prima e quindi aggiungere i valori in un secondo passaggio. Impedirà però il piping dell'output a gzip. –

16

costruzione alla risposta Joe Harris', è possibile utilizzare il filtro @csv in modo che le stringhe siano correttamente citati e sono fuggiti quando necessario:

jq -r '[.case, .custom."speech invoked", .custom."input method"] | @csv' 
+0

Fantastico! Bella scoperta. –

+0

Sembra migliore del join ma produce sempre virgolette doppie attorno ai valori. – eventhorizon

1

Ecco un'altra soluzione. Se data.json contiene i dati di esempio poi

jq -M -s -r 'map(.+.custom|del(.custom)) | (.[0]|keys_unsorted), (.[]|[.[]]) | join(",")' data.json 

produrrà

id,case,speech invoked,input method,session ID 
111,Y,no,hard,420