Comment convertir un fichier CSV en JSON multiligne?

98

Voici mon code, des trucs vraiment 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)

Déclarez certains noms de champs, le lecteur utilise CSV pour lire le fichier et les noms de fichiers pour vider le fichier au format JSON. Voici le problème ...

Chaque enregistrement du fichier CSV se trouve sur une ligne différente. Je veux que la sortie JSON soit de la même manière. Le problème est qu'il dépose tout sur une longue et géante ligne.

J'ai essayé d'utiliser quelque chose comme for line in csvfile:, puis d'exécuter mon code en dessous de celui avec reader = csv.DictReader( line, fieldnames)lequel boucle à travers chaque ligne, mais il fait le fichier entier sur une ligne, puis boucle à travers le fichier entier sur une autre ligne ... continue jusqu'à ce qu'il manque de lignes .

Des suggestions pour corriger cela?

Edit: Pour clarifier, actuellement j'ai: (chaque enregistrement sur la ligne 1)

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

Ce que je recherche: (2 enregistrements sur 2 lignes)

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

Pas chaque champ individuel en retrait / sur une ligne distincte, mais chaque enregistrement sur sa propre ligne.

Quelques exemples d'entrée.

"John","Doe","001","Message1"
"George","Washington","002","Message2"
BeanBagKing
la source
je ne suis pas sûr que votre code fasse exactement ce que vous dites; il ne devrait [{..row..},{..row..},...]pas produire {..row..}{..row..}... C'est-à-dire que la sortie semble être un tableau json d'objets json, pas un flux d'objets json non connectés.
SingleNegationElimination

Réponses:

144

Le problème avec la sortie souhaitée est qu'il ne s'agit pas d'un document json valide ,; c'est un flux de documents json !

Ce n'est pas grave, si c'est ce dont vous avez besoin, mais cela signifie que pour chaque document que vous voulez dans votre sortie, vous devrez appeler json.dumps.

Étant donné que la nouvelle ligne que vous souhaitez séparer de vos documents n'est pas contenue dans ces documents, vous êtes obligé de la fournir vous-même. Il nous suffit donc de retirer la boucle de l'appel à json.dump et d'interposer les retours à la ligne pour chaque document écrit.

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
la source
1
Parfait! Désolé vous avez dû faire un peu de lecture dans les pensées pour l'obtenir, et merci pour les corrections / clarifications. Ceci est exactement ce que je cherchais.
BeanBagKing
4
mais le problème est que outfile n'est pas un json valide
MONTYHS
1
@MONTYHS: La première phrase de cette réponse explique que outfile n'est pas un document json; et ce que c'est à la place. Avez-vous un problème différent de celui de la personne qui a posé cette question?
SingleNegationElimination
6
@ abhi1610: si vous attendez un en-tête dans l'entrée, vous devez construire le DictReadersans donner d' fieldnamesargument; il lira alors la première ligne pour obtenir les noms de champ du fichier.
SingleNegationElimination
1
Et il est bon d'ajouter un encodage pour vos fichiers csvfile = open('file.csv', 'r',encoding='utf-8') et jsonfile = open('file.json', 'w',encoding='utf-8')
Marek Bernád
21

Vous pouvez utiliser Pandas DataFrame pour y parvenir, avec l'exemple suivant:

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
la source
10

J'ai pris la réponse de @ SingleNegationElimination et l'ai simplifiée en un trois lignes pouvant être utilisé dans un 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
la source
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)

Documentation de json.dumps ()

Laxman
la source
6

Vous pouvez essayer ceci

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)

Éditer:

Une approche plus simple

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
la source
3
Je pense que vous devriez au moins mentionner explicitement que vous utilisez un module tiers csvmapper, pour faire cela (et peut-être où l'obtenir), par opposition à quelque chose de intégré.
martineau
2

Ajouter le indentparamètre àjson.dumps

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

Notez également que vous pouvez simplement utiliser json.dumpavec l'open jsonfile:

json.dump(data, jsonfile)
Wayne Werner
la source
Pas tout à fait ce que je recherche. J'ai édité ma question originale pour clarifier et afficher le résultat souhaité. Merci pour le conseil, cela peut être utile plus tard.
BeanBagKing
2

Je vois que c'est vieux mais j'avais besoin du code de SingleNegationElimination, mais j'ai eu un problème avec les données contenant des caractères non utf-8. Celles-ci sont apparues dans des domaines qui ne m'intéressaient pas trop, j'ai donc choisi de les ignorer. Cependant, cela a demandé des efforts. Je suis nouveau sur python, donc avec quelques essais et erreurs, je l'ai fait fonctionner. Le code est une copie de SingleNegationElimination avec la gestion supplémentaire de utf-8. J'ai essayé de le faire avec https://docs.python.org/2.7/library/csv.html mais j'ai finalement abandonné. Le code ci-dessous a fonctionné.

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
la source
1

Que diriez-vous d'utiliser Pandas pour lire le fichier csv dans un DataFrame ( pd.read_csv ), puis de manipuler les colonnes si vous le souhaitez (en les supprimant ou en mettant à jour les valeurs) et enfin en reconvertissant le DataFrame en JSON ( pd.DataFrame.to_json ).

Remarque: je n'ai pas vérifié à quel point cela sera efficace, mais c'est certainement l'un des moyens les plus simples de manipuler et de convertir un grand csv en json.

impiyush
la source
0

En tant que légère amélioration de la réponse @MONTYHS, itération à travers une tup de noms de champ:

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
la source
-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
la source
Quand j'essaye d'utiliser ceci, j'obtiens "KeyError: 'FirstName'". Il ne semble pas que la clé soit ajoutée. Je ne sais pas exactement ce que vous essayez de faire ici, mais je ne pense pas que la sortie corresponde à ce que je recherche puisque vous utilisez le même retrait = 4 que Wayne. À quelle sortie dois-je m'attendre? J'ai édité mon article original pour clarifier ce que je recherche.
BeanBagKing
L'erreur clé est probablement due au fait que ce code ne transmet pas d'argument d'en-tête à DictReader, donc il devine les noms de champ à partir de la première ligne du fichier d'entrée: John, Doe, 5, "None" au lieu de "FirstName, lastname" et ainsi de suite ...
SingleNegationElimination
Meilleure option, celle-ci analyse en fait le CSV pour les champs souhaités (pas seulement dans l'ordre, comme dans la réponse marquée)
GarciadelCastillo
Je reçois une erreur en disantTypeError: expected string or buffer
CodyBugstein