Как преобразовать файл CSV в многострочный JSON?

98

Вот мой код, очень простые вещи ...

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)

Объявите имена некоторых полей, программа чтения использует CSV для чтения файла, а имена полей - для вывода файла в формат JSON. Вот в чем проблема ...

Каждая запись в CSV-файле находится в отдельной строке. Я хочу, чтобы вывод JSON был таким же. Проблема в том, что он сваливает все на одну огромную длинную линию.

Я пробовал использовать что-то вроде, for line in csvfile:а затем запустить свой код ниже этого, с reader = csv.DictReader( line, fieldnames)которым проходит цикл через каждую строку, но он выполняет весь файл в одной строке, затем просматривает весь файл в другой строке ... продолжается, пока не закончатся строки .

Есть предложения по исправлению этого?

Изменить: чтобы уточнить, в настоящее время у меня есть: (каждая запись в строке 1)

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

Что я ищу: (2 записи на 2 строчке)

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

Не каждое отдельное поле с отступом / на отдельной строке, а каждая запись на отдельной строке.

Некоторые образцы ввода.

"John","Doe","001","Message1"
"George","Washington","002","Message2"
BeanBagKing
источник
я не уверен, что ваш код делает именно то , что вы говорите; она должна производить [{..row..},{..row..},...]не {..row..}{..row..}... То есть вывод выглядит так, как будто это будет массив json-объектов json, а не поток несвязанных объектов json.
SingleNegationElimination

Ответы:

145

Проблема с желаемым результатом заключается в том, что это недопустимый документ json; это поток json-документов !

Ничего страшного, если это то, что вам нужно, но это означает, что для каждого документа, который вы хотите выводить, вам придется вызывать json.dumps.

Поскольку символ новой строки, который вы хотите разделить в своих документах, не содержится в этих документах, вы рискуете поставить его самостоятельно. Итак, нам просто нужно вытащить цикл из вызова json.dump и вставить новые строки для каждого написанного документа.

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
источник
1
Отлично! Извините, что вам пришлось немного прочитать мысли, чтобы понять это, и спасибо за исправления / пояснения. Это именно то, что я искал.
BeanBagKing
4
но проблема в том, что Outfile не является допустимым json
MONTYHS 07
1
@MONTYHS: первая отправка этого ответа объясняет, что Outfile не является документом json; и что это вместо этого. Есть ли у вас проблема, отличная от проблемы человека, задавшего этот вопрос?
SingleNegationElimination
6
@ abhi1610: если вы ожидаете заголовок на входе, вы должны DictReaderсоздать без fieldnamesаргумента; Затем он прочитает первую строку, чтобы получить имена полей из файла.
SingleNegationElimination
1
И это хорошо , чтобы добавить кодировку для файлов csvfile = open('file.csv', 'r',encoding='utf-8') и jsonfile = open('file.json', 'w',encoding='utf-8')
Marek Bernad
21

Для этого вы можете использовать Pandas DataFrame в следующем примере:

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)
Науфаль
источник
9

Я взял ответ @ SingleNegationElimination и упростил его до трехстрочного сообщения, которое можно использовать в конвейере:

import csv
import json
import sys

for row in csv.DictReader(sys.stdin):
    json.dump(row, sys.stdout)
    sys.stdout.write('\n')
Лоуренс И. Сиден
источник
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)

Документация json.dumps ()

Лаксман
источник
6

Вы можете попробовать это

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)

Редактировать:

Более простой подход

import csvmapper

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

converter = csvmapper.JSONConverter(parser)

print converter.doConvert(pretty=True)
Трубка S
источник
3
Я думаю, вам следует, по крайней мере, явно упомянуть, что вы используете сторонний модуль csvmapper, чтобы сделать это (и, возможно, где его получить), а не что-то встроенное.
Мартино
2

Добавьте indentпараметр вjson.dumps

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

Также обратите внимание, что вы можете просто использовать json.dumpс открытым jsonfile:

json.dump(data, jsonfile)
Уэйн Вернер
источник
Не совсем то, что я ищу. Я отредактировал свой исходный вопрос, чтобы уточнить и показать желаемый результат. Спасибо за совет, это может пригодиться позже.
BeanBagKing
2

Я вижу, что это старый, но мне нужен код из SingleNegationElimination, однако у меня возникла проблема с данными, содержащими символы, отличные от utf-8. Они появлялись в областях, которые меня не особо интересовали, поэтому я решил проигнорировать их. Однако это потребовало определенных усилий. Я новичок в python, поэтому методом проб и ошибок я заставил его работать. Код является копией SingleNegationElimination с дополнительной обработкой utf-8. Я пытался сделать это с помощью https://docs.python.org/2.7/library/csv.html, но в конце концов сдался. Приведенный ниже код работал.

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

Как насчет использования Pandas для чтения файла csv в DataFrame ( pd.read_csv ), затем манипулирования столбцами, если хотите (удаление их или обновление значений) и, наконец, преобразования DataFrame обратно в JSON ( pd.DataFrame.to_json ).

Примечание: я не проверял, насколько это будет эффективно, но это определенно один из самых простых способов манипулировать и преобразовывать большой csv в json.

Импиюш
источник
0

В качестве небольшого улучшения ответа @MONTYHS, итерация через несколько имен полей:

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)
MONTYHS
источник
Когда я пытаюсь использовать это, я получаю «KeyError: 'FirstName'». Не похоже, что ключ добавляется. Я не совсем уверен, что вы здесь пытаетесь сделать, но я не думаю, что результат соответствует тому, что я ищу, поскольку вы используете тот же indent = 4, что и Уэйн. Какого результата мне ожидать? Я отредактировал свой исходный пост, чтобы уточнить, что я ищу.
BeanBagKing
Ключевая ошибка, скорее всего, связана с тем, что этот код не передает аргумент заголовков DictReader, поэтому он угадывает имена полей из первой строки входного файла: John, Doe, 5, «None» вместо «FirstName, lastname» и так далее ...
SingleNegationElimination
Лучший вариант, этот фактически анализирует CSV для желаемых полей (не только по порядку, как в отмеченном ответе)
GarciadelCastillo
Я получаю сообщение об ошибкеTypeError: expected string or buffer
CodyBugstein