Como transformar o JSON em CSV válido?

6

Estou tentando extrair informações de um arquivo JSON e gravar alguns dos conteúdos em um arquivo CSV.

Aqui está um exemplo do meu texto

"data":{"headers":{"sender":"[email protected]"
"to":"[email protected]"
"subject":"Help with this project"
"x-received-time":"14144273245408"
"received":"from abc.com ()\r\n        by mail.mail.com with SMTP (Postfix)\r\n        for [email protected];\r\n        Mon
"from":"\"Help with this project\" <[email protected]>"
"date":"Mon, 27 Oct 2014 09:03:14 -0500"
"id":"1414427328-2345855-frank"
"to":"[email protected]"
"time":14144273245408
"subject":"Help with this project"
"fromfull":"[email protected]"

Eu quero pegar o conteúdo de: to, fromfull, id, subject, date e gravá-lo em um arquivo csv onde To é a coluna A, fromfull é a coluna B e assim por diante.

Alguém pode oferecer alguma ajuda? Esta é uma resposta JSON.

    
por BeMy Friend 27.10.2014 / 19:04

6 respostas

8

Você pode usar o seguinte comando perl para criar a saída CSV, abrir um terminal e digitar:

perl -n0e '@a= $_ =~ /"date":(".*?").*?"id":(".*?").*?"to":"(.*?)".*?".*?"subject":(".*?").*?"fromfull":"(.*?)"/gs;  while (my @next_n = splice @a, 0, 5) { print join(q{,}, @next_n)."\n"}' inputfile.txt

Ele funcionará mesmo se você tiver vários cabeçalhos em seu arquivo de entrada.

Observe que apenas o último campo "para": é levado em consideração (parece que seus cabeçalhos fornecem as informações duas vezes)

A saída do comando:

"Mon, 27 Oct 2014 09:03:14 -0500","1414427328-2345855-frank",[email protected],"Help with this project",[email protected]
    
por Sylvain Pineau 27.10.2014 / 19:26
8

Você pode converter esse JSON em CSV em uma única linha com jq .

jq '.data.headers | [.sender, .to, .subject, ."x-received-time", 
.received, .from, .date, .id, .to, .subject, .fromfull] 
+ [(.time | tostring)] | join(", ")'

Divisão:

  • .data.headers - Emitir cabeçalhos como um objeto
    • Se os dados continham uma matriz de cabeçalhos, seria .data[].headers
  • […string keys list…] - Emitir valores de string como uma matriz
  • + [(.time | tostring)] - Emita o tempo como uma string e adicione ao array
  • join(", ") - Junte os valores da matriz usando uma vírgula e um espaço
    • Substitua seu delimitador favorito aqui
por Joe Harris 10.11.2014 / 22:21
6

Como você está trabalhando com arquivos JSON, por que não analisá-lo como tal? Instale nodejs-legacy e crie um script NodeJS como:

#!/usr/bin/env node
// parseline.js process lines one by one
'use strict';
var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', function(line){
    var obj = JSON.parse(line);
    // add the fields which you want to extract here:
    var fields = [
        obj.data.headers.to,
        obj.data.headers.subject,
        // etc.
    ];
    // print the fields, joined by a comma (CSV, duh.)
    // No escaping is done, so if the subject contains ',',
    // then you need additional post-processing.
    console.log(fields.join(','));
});

Supondo que você tenha uma string JSON válida em cada linha de um arquivo:

node parseline.js < some.txt

Ou se você realmente quiser ler um único arquivo e analisar campos a partir dele:

#!/usr/bin/env node
// parsefile.js - fully read file and parse some data out of it
'use strict';
var filename = process.argv[1]; // first argument
var fs = require('fs');
var text = fs.readFileSync(filename).toString();
var obj = JSON.parse(text);
// add the fields which you want to extract here:
var fields = [
    obj.data.headers.to,
    obj.data.headers.subject,
    // etc.
];
// print the fields, joined by a comma (CSV, duh.)
// No escaping is done, so if the subject contains ',',
// then you need additional post-processing.
console.log(fields.join(','));

Em seguida, execute-o com:

node parsefile.js yourfile.json > yourfile.csv
    
por Lekensteyn 27.10.2014 / 20:05
2

Você pode usar o jsonv do GitHub

E, em seguida, o seguinte comando:

cat YOUR_JSON_FILEname | jsonv to,fromfull,id,subject,date > output.csv
    
por Edward Moffett 01.01.2017 / 13:56
1

Aqui está um script gawk que acabei de pegar para você!

#!/usr/bin/gawk -f
BEGIN {
  FS="\""
  output=""
  nodata=1
}

/^"data"/{
  if( ! nodata )
  {
    gsub("|$","",output)
    print output
    nodata=0
  }
  output=""
}

/^"[^d][^a][^t][^a]/{
  if ( $2 == "to" || $2 == "fromfull" || $2 == "id" || $2 == "subject" || $2 == "date" )
    output=output$4"|"
}

END{
  gsub("|$","",output)
  print output
}

Ele deve funcionar em um arquivo com várias entradas semelhantes. Se você quiser adicionar outros itens à lista, basta adicioná-los na instrução if. Eu encontrei um problema com seu conjunto de dados: as datas. Eles contêm vírgulas, portanto, não pode ser um verdadeiro CSV. Em vez disso, eu apenas o separei com outro personagem.

    
por Chuck R 27.10.2014 / 20:21
1

Aqui está uma implementação de awk :

   awk -F ":" '{gsub("\"","",$1);key=$1;sub(key " ","");gsub("\","",$0);value[key]=$0; if ("fromfull"== key) print value["from"] ";" value["to"] ";" value["fromfull"] ";" value["id"] ";" value["subject"] ";" value["date"] ;}' jsonFile > csvFile

Este script leu a linha até encontrar a linha "fromfull", em vez de imprimir csv line, por isso deve funcionar também com várias sequências.

Este é o resultado:

  ""Help with this project" <[email protected]>";"[email protected]";"[email protected]";"1414427328-2345855-frank";"Help with this project";"Mon, 27 Oct 2014 09 03 14 -0500"
    
por Lety 27.10.2014 / 20:08