¿Cómo convertir un archivo CSV a JSON multilínea?

98

Aquí está mi código, cosas realmente 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 algunos nombres de campo, el lector usa CSV para leer el archivo y los nombres archivados para volcar el archivo en formato JSON. Aquí está el problema ...

Cada registro del archivo CSV está en una fila diferente. Quiero que la salida JSON sea la misma. El problema es que lo arroja todo en una línea gigante y larga.

Intenté usar algo como for line in csvfile:y luego ejecutar mi código debajo de eso con lo reader = csv.DictReader( line, fieldnames)que recorre cada línea, pero hace todo el archivo en una línea, luego recorre todo el archivo en otra línea ... continúa hasta que se agota las líneas .

¿Alguna sugerencia para corregir esto?

Editar: Para aclarar, actualmente tengo: (todos los registros en la línea 1)

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

Lo que estoy buscando: (2 registros en 2 líneas)

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

No cada campo individual sangrado / en una línea separada, pero cada registro en su propia línea.

Alguna entrada de muestra.

"John","Doe","001","Message1"
"George","Washington","002","Message2"
BeanBagKing
fuente
no estoy seguro de que su código haga exactamente lo que dice; debe producir [{..row..},{..row..},...]no {..row..}{..row..}... Es decir, la salida parece ser una matriz json de objetos json, no un flujo de objetos json no conectados.
SingleNegationElimination

Respuestas:

143

El problema con la salida deseada es que no es un documento json válido; es un flujo de documentos json !

Está bien, si es lo que necesita, pero eso significa que para cada documento que desee en su salida, tendrá que llamar json.dumps .

Dado que la nueva línea que desea que separe sus documentos no está contenida en esos documentos, está en la obligación de proporcionarla usted mismo. Así que solo necesitamos sacar el bucle de la llamada a json.dump e interponer nuevas líneas 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
fuente
1
¡Perfecto! Lamento que hayas tenido que leer un poco la mente para conseguirlo, y gracias por las correcciones / aclaraciones. Esto es exactamente lo que estaba buscando.
BeanBagKing
4
pero el problema es que el
archivo de salida
1
@MONTYHS: La primera expresión de esta respuesta explica que el archivo de salida no es un documento json; y lo que es en cambio. ¿Tiene un problema diferente al de la persona que hizo esta pregunta?
SingleNegationElimination
6
@ abhi1610: si espera un encabezado en la entrada, debe construir el DictReadersin dar un fieldnamesargumento; luego leerá la primera línea para obtener los nombres de campo del archivo.
SingleNegationElimination
1
Y es bueno agregar codificación para sus archivos csvfile = open('file.csv', 'r',encoding='utf-8') y jsonfile = open('file.json', 'w',encoding='utf-8')
Marek Bernád
21

Puede usar Pandas DataFrame para lograr esto, con el siguiente ejemplo:

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
fuente
10

Tomé la respuesta de @ SingleNegationElimination y la simplifiqué en tres líneas que se pueden usar en una canalización:

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
fuente
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)

Documentación de json.dumps ()

Laxman
fuente
6

Puedes probar esto

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:

Enfoque más simple

import csvmapper

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

converter = csvmapper.JSONConverter(parser)

print converter.doConvert(pretty=True)
Esnórquel S
fuente
3
Creo que debería haber, al menos, mencionar explícitamente que está utilizando un módulo de terceros csvmapper, para hacer esto (y tal vez dónde conseguirlo) en lugar de algo integrado.
martineau
2

Agregue el indentparámetro ajson.dumps

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

También tenga en cuenta que, simplemente puede usar json.dumpcon el abierto jsonfile:

json.dump(data, jsonfile)
Wayne Werner
fuente
No es exactamente lo que estoy buscando. Edité mi pregunta original para aclarar y mostrar el resultado deseado. Sin embargo, gracias por la sugerencia, esto puede ser útil más adelante.
BeanBagKing
2

Veo que esto es antiguo, pero necesitaba el código de SingleNegationElimination, sin embargo, tuve un problema con los datos que contenían caracteres que no eran utf-8. Estos aparecieron en campos que no me preocupaban demasiado, así que decidí ignorarlos. Sin embargo, eso requirió un poco de esfuerzo. Soy nuevo en Python, así que con un poco de prueba y error lo hice funcionar. El código es una copia de SingleNegationElimination con el manejo adicional de utf-8. Traté de hacerlo con https://docs.python.org/2.7/library/csv.html pero al final me rendí. El siguiente código funcionó.

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
fuente
1

¿Qué tal usar Pandas para leer el archivo csv en un DataFrame ( pd.read_csv ), luego manipular las columnas si lo desea (soltarlas o actualizar valores) y finalmente convertir el DataFrame de nuevo a JSON ( pd.DataFrame.to_json )?

Nota: No he comprobado qué tan eficiente será esto, pero esta es definitivamente una de las formas más fáciles de manipular y convertir un csv grande a json.

impiyush
fuente
0

Como una ligera mejora a la respuesta de @MONTYHS, iterando a través de una serie de nombres 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)
GarcíadelCastillo
fuente
-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)
MESES
fuente
Cuando intento usar esto, obtengo "KeyError: 'FirstName'". No parece que se esté agregando la clave. No estoy seguro exactamente de lo que está tratando de hacer aquí, pero no creo que el resultado coincida con lo que estoy buscando, ya que usa la misma sangría = 4 que Wayne. ¿Qué resultado debo esperar? Edité mi publicación original para aclarar lo que estoy buscando.
BeanBagKing
Lo más probable es que el error clave se deba a que este código no pasa un argumento de encabezado a DictReader, por lo que está adivinando los nombres de campo de la primera línea del archivo de entrada: John, Doe, 5, "Ninguno" en lugar de "Nombre, apellido" y así sucesivamente ...
SingleNegationElimination
Mejor opción, esta en realidad analiza el CSV para los campos deseados (no solo en orden, como en la respuesta marcada)
GarciadelCastillo
Recibo un error que diceTypeError: expected string or buffer
CodyBugstein