Como posso converter JSON para CSV?
import json
import csv
f = open('data.json')
data = json.load(f)
f.close()
f = open('data.csv')
csv_file = csv.writer(f)
for item in data:
f.writerow(item)
f.close()
No entanto, não funcionou. Estou a usar o Django e o erro que recebi foi:
file' object has no attribute 'writerow'
Então, tentei o seguinte:
import json
import csv
f = open('data.json')
data = json.load(f)
f.close()
f = open('data.csv')
csv_file = csv.writer(f)
for item in data:
csv_file.writerow(item)
f.close()
Então eu recebo o erro:
sequence expected
Amostra do ficheiro json:
[
{
"pk": 22,
"model": "auth.permission",
"fields": {
"codename": "add_logentry",
"name": "Can add log entry",
"content_type": 8
}
},
{
"pk": 23,
"model": "auth.permission",
"fields": {
"codename": "change_logentry",
"name": "Can change log entry",
"content_type": 8
}
},
{
"pk": 24,
"model": "auth.permission",
"fields": {
"codename": "delete_logentry",
"name": "Can delete log entry",
"content_type": 8
}
},
{
"pk": 4,
"model": "auth.permission",
"fields": {
"codename": "add_group",
"name": "Can add group",
"content_type": 2
}
},
{
"pk": 10,
"model": "auth.permission",
"fields": {
"codename": "add_message",
"name": "Can add message",
"content_type": 4
}
}
]
17 answers
Não tenho a certeza se esta questão já está resolvida ou não, mas deixe-me colar o que fiz para referência.
Primeiro, o seu JSON tem objectos aninhados, por isso normalmente não pode ser directamente convertido em CSV. Tens de mudar isso para algo assim.
{
"pk": 22,
"model": "auth.permission",
"codename": "add_logentry",
"content_type": 8,
"name": "Can add log entry"
},
......]
Aqui está o meu código para gerar CSV a partir disso.
import csv
import json
x = """[
{
"pk": 22,
"model": "auth.permission",
"fields": {
"codename": "add_logentry",
"name": "Can add log entry",
"content_type": 8
}
},
{
"pk": 23,
"model": "auth.permission",
"fields": {
"codename": "change_logentry",
"name": "Can change log entry",
"content_type": 8
}
},
{
"pk": 24,
"model": "auth.permission",
"fields": {
"codename": "delete_logentry",
"name": "Can delete log entry",
"content_type": 8
}
}
]"""
x = json.loads(x)
f = csv.writer(open("test.csv", "wb+"))
# Write CSV Header, If you dont need that, remove this line
f.writerow(["pk", "model", "codename", "name", "content_type"])
for x in x:
f.writerow([x["pk"],
x["model"],
x["fields"]["codename"],
x["fields"]["name"],
x["fields"]["content_type"]])
Obterás a saída como:
pk,model,codename,name,content_type
22,auth.permission,add_logentry,Can add log entry,8
23,auth.permission,change_logentry,Can change log entry,8
24,auth.permission,delete_logentry,Can delete log entry,8
def flattenjson( b, delim ):
val = {}
for i in b.keys():
if isinstance( b[i], dict ):
get = flattenjson( b[i], delim )
for j in get.keys():
val[ i + delim + j ] = get[j]
else:
val[i] = b[i]
return val
O resultado de executar este trecho no seu objecto JSON:
flattenjson( {
"pk": 22,
"model": "auth.permission",
"fields": {
"codename": "add_message",
"name": "Can add message",
"content_type": 8
}
}, "__" )
É
{
"pk": 22,
"model": "auth.permission',
"fields__codename": "add_message",
"fields__name": "Can add message",
"fields__content_type": 8
}
Depois de aplicar esta função a cada dict na matriz de entrada de objectos JSON:
input = map( lambda x: flattenjson( x, "__" ), input )
E encontrar os nomes das colunas relevantes:
columns = [ x for row in input for x in row.keys() ]
columns = list( set( columns ) )
Não é difícil passar isto pelo módulo csv.
with open( fname, 'wb' ) as out_file:
csv_w = csv.writer( out_file )
csv_w.writerow( columns )
for i_r in input:
csv_w.writerow( map( lambda x: i_r.get( x, "" ), columns ) )
Espero que isto ajuda!
Com a pandas
biblioteca, isto é tão fácil como usar dois comandos!
pandas.read_json()
Converter uma cadeia de JSON para um objeto pandas (uma série ou dataframe). Então, assumindo que os resultados foram armazenados como df
:
df.to_csv()
Que pode devolver um texto ou escrever directamente para um ficheiro csv.
Com base na verbosidade das respostas anteriores, todos devemos agradecer aos pandas pelo atalho.JSON pode representar uma grande variedade de estruturas de dados -- um "objeto" JS é mais ou menos como um dict Python (com teclas de cadeia), um array JS mais ou menos como uma lista Python, e você pode aninhá-las desde que os elementos finais da "folha" sejam números ou cadeias.
CSV pode representar essencialmente apenas uma tabela 2-D -- opcionalmente com uma primeira linha de" cabeçalhos", ou seja, "nomes de colunas" , que pode tornar a tabela interpretável como uma lista de dictos, em vez da interpretação normal, uma lista de listas (mais uma vez, elementos "folha" podem ser números ou strings).
Então, no caso geral, você não pode traduzir uma estrutura JSON arbitrária para um CSV. Em alguns casos especiais você pode (array of arrays sem mais nidificação; array of objects which all have exactly the same keys). Qual caso especial, se houver, se aplica ao seu problema? Os detalhes da solução dependem do caso especial que você tem. Tendo em conta o facto surpreendente de nem sequer mencionares qual se aplica, suspeito que não ter considerado a restrição, nenhum caso utilizável na verdade se aplica, e seu problema é impossível de resolver. Mas por favor, esclareça!Uma solução genérica que traduz qualquer lista json de objectosplanos para csv.
Passa a entrada.JSON file como primeiro argumento na linha de comando.
import csv, json, sys
input = open(sys.argv[1])
data = json.load(input)
input.close()
output = csv.writer(sys.stdout)
output.writerow(data[0].keys()) # header row
for row in data:
output.writerow(row.values())
Este código deve funcionar para si, assumindo que os seus dados JSON estão num ficheiro chamado data.json
.
import json
import csv
with open("data.json") as file:
data = json.load(file)
with open("data.csv", "w") as file:
csv_file = csv.writer(file)
for item in data:
csv_file.writerow([item['pk'], item['model']] + item['fields'].values())
Será fácil de usar csv.DictWriter()
,a implementação detalhada pode ser assim:
def read_json(filename):
return json.loads(open(filename).read())
def write_csv(data,filename):
with open(filename) as outf:
writer = csv.DictWriter(outf, data[0].keys())
writer.writeheader()
for row in data:
writer.writerow(row)
# implement
write_csv(read_json('test.json'), 'output.csv')
Note que isto assume que todos os seus objectos JSON têm os mesmos campos.
Aqui está a referênciaque pode ajudar-te.
Estava a ter problemas com a solução proposta pelo Dan, mas isto funcionou comigo:
import json
import csv
f = open('test.json')
data = json.load(f)
f.close()
f=csv.writer(open('test.csv','wb+'))
for item in data:
f.writerow([item['pk'], item['model']] + item['fields'].values())
Onde " teste.json " continha o seguinte:
[
{"pk": 22, "model": "auth.permission", "fields":
{"codename": "add_logentry", "name": "Can add log entry", "content_type": 8 } },
{"pk": 23, "model": "auth.permission", "fields":
{"codename": "change_logentry", "name": "Can change log entry", "content_type": 8 } }, {"pk": 24, "model": "auth.permission", "fields":
{"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 8 } }
]
Como mencionado nas respostas anteriores, a dificuldade em converter json para csv é porque um arquivo json pode conter dicionários aninhados e, portanto, ser uma estrutura de dados multidimensional versos um csv que é uma estrutura de dados 2D. No entanto, uma boa maneira de transformar uma estrutura multidimensional para um csv é ter vários CSV que se ligam com chaves primárias.
No seu exemplo, a primeira saída do csv tem as colunas "pk","model","fields" como suas colunas. Valores para "pk", e "model "são fáceis de obter, mas como a coluna" fields "contém um dicionário, Ele deve ser seu próprio csv e porque" codenome "parece ser a chave primária, você pode usar como a entrada para" fields " para completar o primeiro csv. O segundo csv contém o dicionário da coluna "fields" com o nome de código como a chave primária que pode ser usada para amarrar os 2 csvs juntos.
Aqui está uma solução para o seu ficheiro json que converte um dicionário aninhado para 2 csvs.
import csv
import json
def readAndWrite(inputFileName, primaryKey=""):
input = open(inputFileName+".json")
data = json.load(input)
input.close()
header = set()
if primaryKey != "":
outputFileName = inputFileName+"-"+primaryKey
if inputFileName == "data":
for i in data:
for j in i["fields"].keys():
if j not in header:
header.add(j)
else:
outputFileName = inputFileName
for i in data:
for j in i.keys():
if j not in header:
header.add(j)
with open(outputFileName+".csv", 'wb') as output_file:
fieldnames = list(header)
writer = csv.DictWriter(output_file, fieldnames, delimiter=',', quotechar='"')
writer.writeheader()
for x in data:
row_value = {}
if primaryKey == "":
for y in x.keys():
yValue = x.get(y)
if type(yValue) == int or type(yValue) == bool or type(yValue) == float or type(yValue) == list:
row_value[y] = str(yValue).encode('utf8')
elif type(yValue) != dict:
row_value[y] = yValue.encode('utf8')
else:
if inputFileName == "data":
row_value[y] = yValue["codename"].encode('utf8')
readAndWrite(inputFileName, primaryKey="codename")
writer.writerow(row_value)
elif primaryKey == "codename":
for y in x["fields"].keys():
yValue = x["fields"].get(y)
if type(yValue) == int or type(yValue) == bool or type(yValue) == float or type(yValue) == list:
row_value[y] = str(yValue).encode('utf8')
elif type(yValue) != dict:
row_value[y] = yValue.encode('utf8')
writer.writerow(row_value)
readAndWrite("data")
Eu sei que já passou muito tempo desde que esta pergunta foi feita, mas eu pensei que eu poderia adicionar a resposta de todos os outros e compartilhar um post no blog que eu acho que explicar a solução de uma forma muito concisa.
Abra um ficheiro para escrita
employ_data = open('/tmp/EmployData.csv', 'w')
Criar o objecto do escritor csv
csvwriter = csv.writer(employ_data)
count = 0
for emp in emp_data:
if count == 0:
header = emp.keys()
csvwriter.writerow(header)
count += 1
csvwriter.writerow(emp.values())
Certifique-se de fechar o ficheiro para gravar o conteúdo
employ_data.close()
import json
o = json.loads('your json string') # Be careful, o must be a list, each of its objects will make a line of the csv.
def flatten(o, k='/'):
global l, c_line
if isinstance(o, dict):
for key, value in o.items():
flatten(value, k + '/' + key)
elif isinstance(o, list):
for ov in o:
flatten(ov, '')
elif isinstance(o, str):
o = o.replace('\r',' ').replace('\n',' ').replace(';', ',')
if not k in l:
l[k]={}
l[k][c_line]=o
def render_csv(l):
ftime = True
for i in range(100): #len(l[list(l.keys())[0]])
for k in l:
if ftime :
print('%s;' % k, end='')
continue
v = l[k]
try:
print('%s;' % v[i], end='')
except:
print(';', end='')
print()
ftime = False
i = 0
def json_to_csv(object_list):
global l, c_line
l = {}
c_line = 0
for ov in object_list : # Assumes json is a list of objects
flatten(ov)
c_line += 1
render_csv(l)
json_to_csv(o)
Divirtam-se.
Crie um novo ficheiro Python como: json_to_csv.py
Adicionar este código:
import csv, json, sys
#if you are not using utf-8 files, remove the next line
sys.setdefaultencoding("UTF-8")
#check if you pass the input file and output file
if sys.argv[1] is not None and sys.argv[2] is not None:
fileInput = sys.argv[1]
fileOutput = sys.argv[2]
inputFile = open(fileInput)
outputFile = open(fileOutput, 'w')
data = json.load(inputFile)
inputFile.close()
output = csv.writer(outputFile)
output.writerow(data[0].keys()) # header row
for row in data:
output.writerow(row.values())
Depois de adicionar este código, grave o ficheiro e execute no terminal:
Espero que isto te ajude. Até logo!Python json_to_csv.py entrada.saída txt.csv
def flattenjson(self, mp, delim="|"):
ret = []
if isinstance(mp, dict):
for k in mp.keys():
csvs = self.flattenjson(mp[k], delim)
for csv in csvs:
ret.append(k + delim + csv)
elif isinstance(mp, list):
for k in mp:
csvs = self.flattenjson(k, delim)
for csv in csvs:
ret.append(csv)
else:
ret.append(mp)
return ret
Obrigado!
import csv
f = open('data.json')
data = json.load(f)
f.close()
new_data = []
for i in data:
flat = {}
names = i.keys()
for n in names:
try:
if len(i[n].keys()) > 0:
for ii in i[n].keys():
flat[n+"_"+ii] = i[n][ii]
except:
flat[n] = i[n]
new_data.append(flat)
f = open(filename, "r")
writer = csv.DictWriter(f, new_data[0].keys())
writer.writeheader()
for row in new_data:
writer.writerow(row)
f.close()
Uma vez que os dados parecem estar num formato de dicionário, parece que você deve realmente usar csv.DictWriter () para realmente output as linhas com a informação de cabeçalho apropriado. Isto deve permitir que a conversão seja um pouco mais fácil. The fieldnames parameter would then set up the order properly while the output of the first line as the headers would allow it to be read and processed later by csv.DictReader ().
Por exemplo, Mike Repassoutput = csv.writer(sys.stdout)
output.writerow(data[0].keys()) # header row
for row in data:
output.writerow(row.values())
No entanto, basta mudar a configuração inicial para output = csv.DictWriter (filesetting, fieldnames=data[0].chaves ()
Note que uma vez que a ordem dos elementos de um dicionário não está definida, você pode ter que criar entradas de nomes de campo explicitamente. Assim que fizeres isso, o writerow vai funcionar. As escritas então funcionam como mostrado originalmente.
Additionaly eu encontrei o escritor csv estava adicionando um CR extra para o arquivo (eu tenho uma linha vazia para cada linha com dados dentro do arquivo csv). A solução foi muito fácil seguindo a resposta @Jason R. Coombs a este tópico: CSV em Python adicionando um extra regresso da carruagem
Você precisa simplesmente adicionar o parâmetro lineter = '\n ' ao csv.escritor. Será: csv_w = csv.writer( out_file, lineterminator='\n' )
Esta solução deve funcionar em todos os cenários:
def flatten_json(json):
def process_value(keys, value, flattened):
if isinstance(value, dict):
for key in value.keys():
process_value(keys + [key], value[key], flattened)
elif isinstance(value, list):
for idx, v in enumerate(value):
process_value(keys + [str(idx)], v, flattened)
else:
flattened['__'.join(keys)] = value
flattened = {}
for key in json.keys():
process_value([key], json[key], flattened)
return flattened