Я пытаюсь создать файл JSON через CSV. Код ниже создает данные, но не совсем там, где я хочу. У меня есть некоторый опыт работы с питоном. Насколько я понимаю, файл JSON должен быть написан так [{}, {}, ..., {}].
Как мне?:
Я могу вставить ",", но как удалить последний ","?
Как мне вставить "[" в самом начале и "]" в самом конце? Я попытался вставить его в outputfile.write ('[' ... и т. д.), Он отображается слишком во многих местах.
Не включать заголовок в первую строку файла json.
Names.csv:
id,team_name,team_members
123,Biology,"Ali Smith, Jon Doe"
234,Math,Jane Smith
345,Statistics ,"Matt P, Albert Shaw"
456,Chemistry,"Andrew M, Matt Shaw, Ali Smith"
678,Physics,"Joe Doe, Jane Smith, Ali Smith "
Код:
import csv
import json
import os
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
for line in infile:
row = dict()
# print(row)
id, team_name, *team_members = line.split(',')
row["id"] = id;
row["team_name"] = team_name;
row["team_members"] = team_members
json.dump(row,outfile)
outfile.write("," + "\n" )
Результат на данный момент:
{"id": "id", "team_name": "team_name", "team_members": ["team_members\n"]},
{"id": "123", "team_name": "Biology", "team_members": ["\"Ali Smith", " Jon Doe\"\n"]},
{"id": "234", "team_name": "Math", "team_members": ["Jane Smith \n"]},
{"id": "345", "team_name": "Statistics ", "team_members": ["\"Matt P", " Albert Shaw\"\n"]},
{"id": "456", "team_name": "Chemistry", "team_members": ["\"Andrew M", " Matt Shaw", " Ali Smith\"\n"]},
{"id": "678", "team_name": "Physics", "team_members": ["\"Joe Doe", " Jane Smith", " Ali Smith \""]},
Вы должны поместить эти словари в массив (в Python обычно называется списком). После этого вы выводите.
Вам нужен правильный массив JSON ([{}, {}, {}]
) или поток объектов JSON ({} {} {}
). Последовательность объектов, разделенных запятыми, недопустима.
Что такое выход желанный? Должны ли члены команды быть разделены на свой собственный массив?
@chepner Да, у членов команды должен быть свой список / массив, разделенный запятыми.
С минимальным редактированием кода вы можете создать список словарей на Python и сразу выгрузить его в файл как JSON (при условии, что ваш набор данных достаточно мал, чтобы поместиться в памяти):
import csv
import json
import os
rows = [] # Create list
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
for line in infile:
row = dict()
id, team_name, *team_members = line.split(',')
row["id"] = id;
row["team_name"] = team_name;
row["team_members"] = team_members
rows.append(row) # Append row to list
json.dump(rows[1:], outfile) # Write entire list to file (except first row)
Кстати, вы не должны использовать id
в качестве имени переменной в Python, поскольку это встроенная функция.
Панды с легкостью справятся с этим:
df = pd.read_csv('names.csv', dtype=str)
df['team_members'] = (df['team_members']
.map(lambda s: s.split(','))
.map(lambda l: [x.strip() for x in l]))
records = df.to_dict('records')
json.dump(records, outfile)
Во-первых, как пропустить заголовок? Это просто:
next(infile) # skip the first line
for line in infile:
Однако вы можете рассмотреть возможность использования csv.DictReader
для ввода. Он обрабатывает чтение строки заголовка и использование информации в ней для создания словаря для каждой строки и разделения строк для вас (а также обработки случаев, о которых вы, возможно, не задумывались, например, цитируемый или экранированный текст, который может присутствовать в CSV файлы):
for row in csv.DictReader(infile):
jsondump(row,outfile)
Теперь о более сложной проблеме.
Лучшим решением, вероятно, было бы использование итеративной библиотеки JSON, которая может выгружать итератор в виде массива JSON. Тогда вы могли бы сделать что-то вроде этого:
def rows(infile):
for line in infile:
row = dict()
# print(row)
id, team_name, *team_members = line.split(',')
row["id"] = id;
row["team_name"] = team_name;
row["team_members"] = team_members
yield row
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
genjson.dump(rows(infile), outfile)
В документации stdlib json.JSONEncoder
есть пример, который делает именно это - хотя и не очень эффективно, потому что сначала использует весь итератор для построения списка, а затем выводит его:
class GenJSONEncoder(json.JSONEncoder):
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, o)
j = GenJSONEncoder()
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
outfile.write(j.encode(rows(infile)))
И действительно, если вы хотите создать весь список, а не кодировать построчно, может быть проще просто явно выполнить литификацию:
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
json.dump(list(rows(infile)))
Вы можете пойти дальше, также переопределив метод iterencode
, но это будет намного менее тривиально, и вы, вероятно, захотите найти эффективную, хорошо протестированную потоковую итеративную библиотеку JSON на PyPI вместо того, чтобы создавать ее самостоятельно из модуля json
. .
Но, тем временем, вот прямое решение вашего вопроса, как можно меньше меняя существующий код:
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
# print the opening [
outfile.write('[\n')
# keep track of the index, just to distinguish line 0 from the rest
for i, line in enumerate(infile):
row = dict()
# print(row)
id, team_name, *team_members = line.split(',')
row["id"] = id;
row["team_name"] = team_name;
row["team_members"] = team_members
# add the ,\n _before_ each row except the first
if i:
outfile.write(',\n')
json.dump(row,outfile)
# write the final ]
outfile.write('\n]')
Этот трюк - особый подход к первому элементу, а не к последнему - упрощает многие проблемы этого типа.
Другой способ упростить задачу - перебрать соседние пары строк, используя небольшую вариацию примера pairwise
в документации itertools
:
def pairwise(iterable):
a, b = itertools.tee(iterable)
next(b, None)
return itertools.zip_longest(a, b, fillvalue=None)
with open('names.csv', 'r') as infile, open('names1.json','w') as outfile:
# print the opening [
outfile.write('[\n')
# iterate pairs of lines
for line, nextline in pairwise(infile):
row = dict()
# print(row)
id, team_name, *team_members = line.split(',')
row["id"] = id;
row["team_name"] = team_name;
row["team_members"] = team_members
json.dump(row,outfile)
# add the , if there is a next line
if nextline is not None:
outfile.write(',')
outfile.write('\n')
# write the final ]
outfile.write(']')
Это так же эффективно, как и предыдущая версия, и концептуально проще, но гораздо более абстрактно.
Что делать, если я не могу проверить nextline
, потому что это зависит не от меня? Некоторые фреймворки делают это за меня, если мой элемент - True
, он вызывает мою функцию, которая записывает данные в файл. У меня есть несколько файлов, и в зависимости от типа элемента я должен записать их в конкретный файл.
Похоже, было бы намного проще использовать класс csv.DictReader
вместо того, чтобы изобретать колесо:
import csv
import json
data = []
with open('names.csv', 'r', newline='') as infile:
for row in csv.DictReader(infile):
data.append(row)
with open('names1.json','w') as outfile:
json.dump(data, outfile, indent=4)
Содержимое файла names1.json
после выполнения (я использовал indent=4
, чтобы сделать его более читабельным):
[
{
"id": "123",
"team_name": "Biology",
"team_members": "Ali Smith, Jon Doe"
},
{
"id": "234",
"team_name": "Math",
"team_members": "Jane Smith"
},
{
"id": "345",
"team_name": "Statistics ",
"team_members": "Matt P, Albert Shaw"
},
{
"id": "456",
"team_name": "Chemistry",
"team_members": "Andrew M, Matt Shaw, Ali Smith"
},
{
"id": "678",
"team_name": "Physics",
"team_members": "Joe Doe, Jane Smith, Ali Smith"
}
]
С
pandas
это было бы очень просто ...