The Skinny
jq -r '(.[0] | keys_unsorted) as $keys | $keys, map([.[ $keys[] ]])[] | @csv'
ou:
jq -r '(.[0] | keys_unsorted) as $keys | ([$keys] + map([.[ $keys[] ]])) [] | @csv'
Os detalhes
a parte, de lado
Descrever os detalhes é complicado porque jq é orientado a fluxo, o que significa que opera em uma sequência de dados JSON, em vez de um único valor. O fluxo JSON de entrada é convertido em algum tipo interno que é passado pelos filtros e, em seguida, codificado em um fluxo de saída no final do programa. O tipo interno não é modelado por JSON e não existe como um tipo nomeado. É mais facilmente demonstrado examinando a saída de um índice simples ( .[]
) ou do operador vírgula (examiná-lo diretamente poderia ser feito com um depurador, mas isso seria em termos de tipos de dados internos de jq, em vez dos tipos de dados conceituais por trás de JSON) .
$ jq -c '. []' <<< '["a", "b"]'
"uma"
"b"
$ jq -cn '"a", "b"'
"uma"
"b"
Observe que a saída não é uma matriz (o que seria ["a", "b"]
). A saída compacta (a -c
opção) mostra que cada elemento da matriz (ou argumento do ,
filtro) se torna um objeto separado na saída (cada um está em uma linha separada).
Um stream é como um JSON-seq , mas usa novas linhas em vez de RS como separador de saída quando codificado. Consequentemente, este tipo interno é referido pelo termo genérico "sequência" nesta resposta, com "fluxo" sendo reservado para a entrada e saída codificadas.
Construindo o Filtro
As chaves do primeiro objeto podem ser extraídas com:
.[0] | keys_unsorted
Geralmente, as chaves são mantidas em sua ordem original, mas a preservação da ordem exata não é garantida. Conseqüentemente, eles precisarão ser usados para indexar os objetos para obter os valores na mesma ordem. Isso também evitará que os valores estejam nas colunas erradas se alguns objetos tiverem uma ordem de chave diferente.
Para gerar as chaves como a primeira linha e torná-las disponíveis para indexação, elas são armazenadas em uma variável. O próximo estágio do pipeline faz referência a essa variável e usa o operador vírgula para anexar o cabeçalho ao fluxo de saída.
(.[0] | keys_unsorted) as $keys | $keys, ...
A expressão após a vírgula é um pouco complicada. O operador de índice em um objeto pode receber uma sequência de strings (por exemplo "name", "value"
), retornando uma sequência de valores de propriedade para essas strings. $keys
é uma matriz, não uma sequência, então []
é aplicada para convertê-la em uma sequência,
$keys[]
que pode então ser passado para .[]
.[ $keys[] ]
Isso também produz uma sequência, portanto, o construtor de matriz é usado para convertê-la em uma matriz.
[.[ $keys[] ]]
Esta expressão deve ser aplicada a um único objeto. map()
é usado para aplicá-lo a todos os objetos na matriz externa:
map([.[ $keys[] ]])
Por último, para este estágio, isso é convertido em uma sequência para que cada item se torne uma linha separada na saída.
map([.[ $keys[] ]])[]
Por que agrupar a sequência em um array dentro do map
apenas para descompactá-la fora? map
produz uma matriz; .[ $keys[] ]
produz uma sequência. Aplicar map
à sequência de .[ $keys[] ]
produziria uma matriz de sequências de valores, mas como as sequências não são do tipo JSON, em vez disso, você obtém uma matriz achatada contendo todos os valores.
["NSW","AU","state","New South Wales","AB","CA","province","Alberta","ABD","GB","council area","Aberdeenshire","AK","US","state","Alaska"]
Os valores de cada objeto precisam ser mantidos separados, para que se tornem linhas separadas na saída final.
Por fim, a sequência é passada pelo @csv
formatador.
Alternar
Os itens podem ser separados mais tarde, em vez de mais cedo. Em vez de usar o operador vírgula para obter uma sequência (passando uma sequência como o operando correto), a sequência de cabeçalho ( $keys
) pode ser agrupada em uma matriz e +
usada para anexar a matriz de valores. Isso ainda precisa ser convertido em uma sequência antes de ser passado para @csv
.
json2csv
está em stackoverflow.com/questions/57242240/…