Como converter arquivo CSV para JSON multilinha?

98

Aqui está meu código, coisas muito simples ...

import csv
import json

csvfile = open('file.csv', 'r')
jsonfile = open('file.json', 'w')

fieldnames = ("FirstName","LastName","IDNumber","Message")
reader = csv.DictReader( csvfile, fieldnames)
out = json.dumps( [ row for row in reader ] )
jsonfile.write(out)

Declare alguns nomes de campo, o leitor usa CSV para ler o arquivo e os nomes de arquivo para despejar o arquivo em um formato JSON. Aqui está o problema ...

Cada registro no arquivo CSV está em uma linha diferente. Eu quero que a saída JSON seja da mesma maneira. O problema é que despeja tudo em uma longa linha gigante.

Eu tentei usar algo como for line in csvfile:e, em seguida, executar meu código abaixo daquele com o reader = csv.DictReader( line, fieldnames)qual faz um loop em cada linha, mas ele faz o arquivo inteiro em uma linha e, em seguida, executa o loop em todo o arquivo em outra linha ... continua até ficar sem linhas .

Alguma sugestão para corrigir isso?

Editar: Para esclarecer, atualmente eu tenho: (todos os registros na linha 1)

[{"FirstName":"John","LastName":"Doe","IDNumber":"123","Message":"None"},{"FirstName":"George","LastName":"Washington","IDNumber":"001","Message":"Something"}]

O que procuro: (2 registros em 2 linhas)

{"FirstName":"John","LastName":"Doe","IDNumber":"123","Message":"None"}
{"FirstName":"George","LastName":"Washington","IDNumber":"001","Message":"Something"}

Não cada campo individual indentado / em uma linha separada, mas cada registro em sua própria linha.

Alguns exemplos de entrada.

"John","Doe","001","Message1"
"George","Washington","002","Message2"
BeanBagKing
fonte
não tenho certeza se seu código faz exatamente o que você diz; deve produzir [{..row..},{..row..},...]não {..row..}{..row..}... Ou seja, a saída parece ser uma matriz json de objetos json, não um fluxo de objetos json não conectados.
SingleNegationElimination

Respostas:

144

O problema com a saída desejada é que não é um documento json válido; é um fluxo de documentos json !

Tudo bem, se for o que você precisa, mas isso significa que, para cada documento que quiser em sua saída, você terá que chamar json.dumps.

Como a nova linha que você deseja separar seus documentos não está contida nesses documentos, você mesmo deve fornecê-la. Portanto, precisamos apenas retirar o loop da chamada para json.dump e interpor novas linhas para cada documento escrito.

import csv
import json

csvfile = open('file.csv', 'r')
jsonfile = open('file.json', 'w')

fieldnames = ("FirstName","LastName","IDNumber","Message")
reader = csv.DictReader( csvfile, fieldnames)
for row in reader:
    json.dump(row, jsonfile)
    jsonfile.write('\n')
SingleNegationElimination
fonte
1
Perfeito! Desculpe, você teve que fazer um pouco de leitura de mente para obtê-lo, e obrigado pelas correções / esclarecimentos. Isso é exatamente o que eu estava procurando.
BeanBagKing
4
mas o problema é que o
arquivo de saída
1
@MONTYHS: A primeira sentença desta resposta explica que outfile não é um documento json; e o que é em vez disso. Você está tendo um problema diferente daquele da pessoa que fez essa pergunta?
SingleNegationElimination
6
@ abhi1610: se você está esperando um cabeçalho na entrada, deve construir o DictReadersem fornecer um fieldnamesargumento; ele então lerá a primeira linha para obter os nomes dos campos do arquivo.
SingleNegationElimination
1
E é bom adicionar codificação para seus arquivos csvfile = open('file.csv', 'r',encoding='utf-8') e jsonfile = open('file.json', 'w',encoding='utf-8')
Marek Bernád
21

Você pode usar o Pandas DataFrame para fazer isso, com o seguinte exemplo:

import pandas as pd
csv_file = pd.DataFrame(pd.read_csv("path/to/file.csv", sep = ",", header = 0, index_col = False))
csv_file.to_json("/path/to/new/file.json", orient = "records", date_format = "epoch", double_precision = 10, force_ascii = True, date_unit = "ms", default_handler = None)
Naufal
fonte
10

Peguei a resposta de @ SingleNegationElimination e a simplifiquei em um três linhas que pode ser usado em um pipeline:

import csv
import json
import sys

for row in csv.DictReader(sys.stdin):
    json.dump(row, sys.stdout)
    sys.stdout.write('\n')
Lawrence I. Siden
fonte
8
import csv
import json

file = 'csv_file_name.csv'
json_file = 'output_file_name.json'

#Read CSV File
def read_CSV(file, json_file):
    csv_rows = []
    with open(file) as csvfile:
        reader = csv.DictReader(csvfile)
        field = reader.fieldnames
        for row in reader:
            csv_rows.extend([{field[i]:row[field[i]] for i in range(len(field))}])
        convert_write_json(csv_rows, json_file)

#Convert csv data into json
def convert_write_json(data, json_file):
    with open(json_file, "w") as f:
        f.write(json.dumps(data, sort_keys=False, indent=4, separators=(',', ': '))) #for pretty
        f.write(json.dumps(data))


read_CSV(file,json_file)

Documentação de json.dumps ()

Laxman
fonte
6

Você pode tentar isso

import csvmapper

# how does the object look
mapper = csvmapper.DictMapper([ 
  [ 
     { 'name' : 'FirstName'},
     { 'name' : 'LastName' },
     { 'name' : 'IDNumber', 'type':'int' },
     { 'name' : 'Messages' }
  ]
 ])

# parser instance
parser = csvmapper.CSVParser('sample.csv', mapper)
# conversion service
converter = csvmapper.JSONConverter(parser)

print converter.doConvert(pretty=True)

Editar:

Abordagem mais simples

import csvmapper

fields = ('FirstName', 'LastName', 'IDNumber', 'Messages')
parser = CSVParser('sample.csv', csvmapper.FieldMapper(fields))

converter = csvmapper.JSONConverter(parser)

print converter.doConvert(pretty=True)
Snork S
fonte
3
Eu acho que você deveria ter, pelo menos, explicitamente mencionado que está usando um módulo de terceiros,, csvmapperpara fazer isso (e talvez onde obtê-lo) em vez de algo integrado.
martineau
2

Adicione o indentparâmetro ajson.dumps

 data = {'this': ['has', 'some', 'things'],
         'in': {'it': 'with', 'some': 'more'}}
 print(json.dumps(data, indent=4))

Observe também que você pode simplesmente usar json.dumpcom o aberto jsonfile:

json.dump(data, jsonfile)
Wayne Werner
fonte
Não é bem o que estou procurando. Editei minha pergunta original para esclarecer e mostrar a saída desejada. Obrigado pela dica, porém, isso pode ser útil mais tarde.
BeanBagKing
2

Vejo que isso é antigo, mas eu precisava do código de SingleNegationElimination, no entanto, tive problemas com os dados que não continham caracteres utf-8. Eles apareceram em campos com os quais eu não estava muito preocupado, então optei por ignorá-los. No entanto, isso exigiu algum esforço. Eu sou novo no python, então, com algumas tentativas e erros, consegui fazê-lo funcionar. O código é uma cópia de SingleNegationElimination com o tratamento extra de utf-8. Tentei fazer isso com https://docs.python.org/2.7/library/csv.html, mas no final desisti. O código abaixo funcionou.

import csv, json

csvfile = open('file.csv', 'r')
jsonfile = open('file.json', 'w')

fieldnames = ("Scope","Comment","OOS Code","In RMF","Code","Status","Name","Sub Code","CAT","LOB","Description","Owner","Manager","Platform Owner")
reader = csv.DictReader(csvfile , fieldnames)

code = ''
for row in reader:
    try:
        print('+' + row['Code'])
        for key in row:
            row[key] = row[key].decode('utf-8', 'ignore').encode('utf-8')      
        json.dump(row, jsonfile)
        jsonfile.write('\n')
    except:
        print('-' + row['Code'])
        raise
Mark Channing
fonte
1

Que tal usar o Pandas para ler o arquivo csv em um DataFrame ( pd.read_csv ), em seguida, manipular as colunas se quiser (descartando-as ou atualizando os valores) e, finalmente, converter o DataFrame de volta para JSON ( pd.DataFrame.to_json ).

Observação: não verifiquei a eficiência disso, mas essa é definitivamente uma das maneiras mais fáceis de manipular e converter um csv grande em json.

impiyush
fonte
0

Como uma pequena melhoria na resposta @MONTYHS, iterando por meio de um conjunto de nomes de campo:

import csv
import json

csvfilename = 'filename.csv'
jsonfilename = csvfilename.split('.')[0] + '.json'
csvfile = open(csvfilename, 'r')
jsonfile = open(jsonfilename, 'w')
reader = csv.DictReader(csvfile)

fieldnames = ('FirstName', 'LastName', 'IDNumber', 'Message')

output = []

for each in reader:
  row = {}
  for field in fieldnames:
    row[field] = each[field]
output.append(row)

json.dump(output, jsonfile, indent=2, sort_keys=True)
GarciadelCastillo
fonte
-1
import csv
import json
csvfile = csv.DictReader('filename.csv', 'r'))
output =[]
for each in csvfile:
    row ={}
    row['FirstName'] = each['FirstName']
    row['LastName']  = each['LastName']
    row['IDNumber']  = each ['IDNumber']
    row['Message']   = each['Message']
    output.append(row)
json.dump(output,open('filename.json','w'),indent=4,sort_keys=False)
MONTYHS
fonte
Quando tento usar isso, obtenho "KeyError: 'FirstName'". Não parece que a chave está sendo adicionada. Não sei exatamente o que você está tentando fazer aqui, mas não acho que a saída corresponda ao que estou procurando, pois você usa o mesmo indent = 4 que Wayne. Qual saída devo esperar? Eu editei minha postagem original para esclarecer o que estou procurando.
BeanBagKing
O erro de chave é mais provável porque este código não passa um argumento de cabeçalho para DictReader, portanto, ele adivinha os nomes dos campos da primeira linha do arquivo de entrada: John, Doe, 5, "None" em vez de "FirstName, lastname," e assim por diante ...
SingleNegationElimination
Melhor opção, esta realmente analisa o CSV para os campos desejados (não apenas na ordem, como na resposta marcada)
GarciadelCastillo
Recebo um erro dizendoTypeError: expected string or buffer
CodyBugstein