Jak przekonwertować plik CSV na wielowierszowy JSON?


98

Oto mój kod, naprawdę proste rzeczy ...

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)

Zadeklaruj niektóre nazwy pól, czytnik używa CSV do odczytania pliku, a nazwy plików do zrzucenia pliku do formatu JSON. Oto problem ...

Każdy rekord w pliku CSV znajduje się w innym wierszu. Chcę, aby dane wyjściowe JSON były takie same. Problem w tym, że zrzuca to wszystko na jedną wielką, długą linię.

Próbowałem użyć czegoś podobnego, for line in csvfile:a następnie uruchomić mój kod poniżej tego, z reader = csv.DictReader( line, fieldnames)którym pętla przechodzi przez każdą linię, ale robi cały plik w jednej linii, a następnie przechodzi przez cały plik w innej linii ... kontynuuje, dopóki nie skończą się linie .

Jakieś sugestie, jak to naprawić?

Edycja: Aby wyjaśnić, obecnie mam: (każdy rekord w linii 1)

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

Czego szukam: (2 rekordy w 2 liniach)

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

Nie każde pole jest wcięte / w osobnym wierszu, ale każdy rekord w osobnym wierszu.

Niektóre przykładowe dane wejściowe.

"John","Doe","001","Message1"
"George","Washington","002","Message2"

nie jestem pewien, czy twój kod robi dokładnie to , co mówisz; powinien produkować [{..row..},{..row..},...]nie {..row..}{..row..}... Oznacza to, że dane wyjściowe wyglądają na tablicę json obiektów json, a nie strumień niepołączonych obiektów json.
SingleNegationElimination

Odpowiedzi:


146

Problem z żądanym wyjściem polega na tym, że nie jest to prawidłowy dokument json; to strumień dokumentów json !

W porządku, jeśli tego potrzebujesz, ale oznacza to, że dla każdego dokumentu, który chcesz umieścić w wyniku, będziesz musiał zadzwonić json.dumps.

Ponieważ nowa linia, którą chcesz oddzielić dokumenty, nie jest zawarta w tych dokumentach, jesteś na haku, aby dostarczyć go samodzielnie. Musimy więc po prostu wyciągnąć pętlę z wywołania json.dump i wstawić nowe linie dla każdego napisanego dokumentu.

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

1
Idealny! Przepraszam, że musiałeś trochę poczytać w myślach, aby to uzyskać, i dzięki za poprawki / wyjaśnienia. To jest dokładnie to, czego szukałem.
BeanBagKing

4
ale problem polega na tym, że plik
wyjściowy

1
@MONTYHS: Pierwsze zdanie tej odpowiedzi wyjaśnia, że ​​plik wyjściowy nie jest dokumentem json; i co to jest zamiast tego. Czy masz inny problem niż osoba, która zadała to pytanie?
SingleNegationElimination

6
@ abhi1610: jeśli spodziewasz się nagłówka na wejściu, powinieneś skonstruować DictReaderbez podawania fieldnamesargumentu; odczyta wtedy pierwszą linię, aby pobrać nazwy pól z pliku.
SingleNegationElimination

2
I dobrze jest dodać kodowanie do swoich plików csvfile = open('file.csv', 'r',encoding='utf-8') i jsonfile = open('file.json', 'w',encoding='utf-8')
Marek Bernád

22

Aby to osiągnąć, możesz użyć Pandas DataFrame, korzystając z następującego przykładu:

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)

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

Dokumentacja json.dumps ()


Prawidłowa odpowiedź, imho.
JohnnyFromBF

9

Wziąłem odpowiedź @ SingleNegationElimination i uprościłem ją do trzyliniówki, której można używać w potoku:

import csv
import json
import sys

for row in csv.DictReader(sys.stdin):
    json.dump(row, sys.stdout)
    sys.stdout.write('\n')

6

Możesz tego spróbować

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)

Edytować:

Prostsze podejście

import csvmapper

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

converter = csvmapper.JSONConverter(parser)

print converter.doConvert(pretty=True)

3
Myślę, że powinieneś przynajmniej wyraźnie wspomnieć, że używasz modułu innej firmy csvmapper, aby to zrobić (i być może skąd go wziąć), w przeciwieństwie do czegoś wbudowanego.
martineau

2

Dodaj indentparametr dojson.dumps

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

Pamiętaj również, że możesz po prostu użyć json.dumpz otwartym jsonfile:

json.dump(data, jsonfile)

Niezupełnie to, czego szukam. Zredagowałem moje oryginalne pytanie, aby wyjaśnić i pokazać pożądane wyniki. Dziękuję jednak za wskazówkę, może się później przydać.
BeanBagKing

2

Widzę, że to jest stare, ale potrzebowałem kodu z SingleNegationElimination, ale miałem problem z danymi zawierającymi znaki inne niż utf-8. Pojawiły się one w dziedzinach, którymi się specjalnie nie przejmowałem, więc zdecydowałem się je zignorować. Jednak wymagało to pewnego wysiłku. Jestem nowy w Pythonie, więc dzięki kilku próbom i błędom udało mi się go uruchomić. Kod jest kopią SingleNegationElimination z dodatkową obsługą utf-8. Próbowałem to zrobić z https://docs.python.org/2.7/library/csv.html, ale w końcu się poddałem. Poniższy kod zadziałał.

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

1

Co powiesz na użycie Pandas do odczytania pliku csv do DataFrame ( pd.read_csv ), a następnie manipulowanie kolumnami, jeśli chcesz (upuszczanie ich lub aktualizowanie wartości) i wreszcie przekonwertowanie DataFrame z powrotem na JSON ( pd.DataFrame.to_json ).

Uwaga: nie sprawdziłem, jak wydajne będzie to, ale jest to zdecydowanie jeden z najłatwiejszych sposobów manipulowania i konwertowania dużego pliku csv na json.


0

Jako niewielkie ulepszenie odpowiedzi @MONTYHS, iteracja przez krotkę nazw pól:

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)

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

Kiedy próbuję tego użyć, otrzymuję komunikat „KeyError: 'FirstName'”. Wygląda na to, że klucz nie jest dodawany. Nie jestem pewien, co dokładnie próbujesz tutaj zrobić, ale nie sądzę, aby wynik był zgodny z tym, czego szukam, ponieważ używasz tego samego wcięcia = 4 co Wayne. Jakiego wyniku powinienem się spodziewać? Zredagowałem mój oryginalny post, aby wyjaśnić, czego szukam.
BeanBagKing

Błąd klucza jest najprawdopodobniej spowodowany tym, że ten kod nie przekazuje argumentu nagłówka do DictReader, więc zgaduje nazwy pól z pierwszego wiersza pliku wejściowego: Jan, Kowalski, 5, „Brak” zamiast „Imię, nazwisko” i tak dalej ...
SingleNegationElimination

Lepsza opcja, ten faktycznie analizuje plik CSV pod kątem żądanych pól (nie tylko w kolejności, jak w zaznaczonej odpowiedzi)
GarciadelCastillo

TypeError: expected string or buffer
Pojawia
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.