Comment convertir JSON en CSV?
j'ai un fichier JSON que je veux cacher à un fichier CSV. Comment je peux faire ça avec Python?
j'ai essayé:
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()
cependant, il n'a pas fonctionné. J'utilise Django et l'erreur que j'ai reçue est:
file' object has no attribute 'writerow'
alors, j'ai essayé la suivante:
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()
j'obtiens alors l'erreur:
sequence expected
exemple de fichier 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 réponses
Je ne suis pas sûr que cette question soit déjà résolue ou pas, mais laissez-moi coller ce que j'ai fait pour référence.
tout d'abord, votre JSON a des objets imbriqués, de sorte qu'il ne peut normalement pas être directement converti en CSV. Vous devez changer cela à quelque chose comme ceci:
{
"pk": 22,
"model": "auth.permission",
"codename": "add_logentry",
"content_type": 8,
"name": "Can add log entry"
},
......]
Voici mon code pour générer CSV à partir de cela:
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"]])
vous obtiendrez la sortie comme:
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
je suppose que votre fichier JSON sera décodé dans une liste de dictionnaires. Tout d'abord, nous avons besoin d'une fonction qui aplatira les objets JSON:
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
le résultat de l'exécution de cet extrait sur votre objet JSON:
flattenjson( {
"pk": 22,
"model": "auth.permission",
"fields": {
"codename": "add_message",
"name": "Can add message",
"content_type": 8
}
}, "__" )
est
{
"pk": 22,
"model": "auth.permission',
"fields__codename": "add_message",
"fields__name": "Can add message",
"fields__content_type": 8
}
après avoir appliqué cette fonction à chaque dict dans le tableau d'entrées des objets JSON:
input = map( lambda x: flattenjson( x, "__" ), input )
et trouver les noms des colonnes correspondantes:
columns = [ x for row in input for x in row.keys() ]
columns = list( set( columns ) )
il n'est pas difficile d'exécuter ceci à travers le module 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 ) )
j'espère que cela aidera!
avec le pandas
bibliothèque , c'est aussi facile qu'utiliser deux commandes!
pandas.read_json()
pour convertir une chaîne JSON en un objet pandas (soit une série, soit une base de données). Ensuite, en supposant que les résultats ont été stockés comme df
:
df.to_csv()
qui peut retourner une chaîne de caractères ou écrire directement dans un fichier csv.
basé sur la verbosité des réponses précédentes, nous on devrait remercier pandas pour le raccourci.
JSON peut représenter une grande variété de structures de données -- un" objet "JS est à peu près comme un dict Python (avec des clés string), un" tableau "JS à peu près comme une liste Python, et vous pouvez les emboîter aussi longtemps que les éléments" leaf " finaux sont des nombres ou des chaînes.
CSV peut essentiellement ne représentent qu'un 2-D de la table -- en option avec une première ligne de "en-têtes", c'est à dire, "les noms de colonnes", ce qui peut rendre la table interprétable comme une liste des dicts, au lieu de l'interprétation normale, un liste des listes (encore une fois, les éléments "leaf" peuvent être des nombres ou des chaînes).
donc, dans le cas général, vous ne pouvez pas traduire une structure JSON arbitraire en CSV. Dans quelques cas spéciaux, vous pouvez (tableau de tableaux, sans plus de nidification; les tableaux d'objets qui ont tous exactement les mêmes touches). Qui cas particulier, le cas échéant, s'applique à votre problème? Les détails de la solution dépendent du cas particulier que vous avez. Étant donné le fait étonnant que vous ne mentionnent même pas lequel s'applique, je soupçonne que vous n'avez peut-être pas considéré la contrainte, ni l'un ni l'autre cas utilisable en fait s'applique, et votre problème est impossible à résoudre. Mais s'il vous plaît préciser!
une solution générique qui traduit n'importe quelle liste json des objets flat vers csv.
passez l'entrée.JSON file comme premier argument en ligne de commande.
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())
ce code devrait fonctionner pour vous, en supposant que vos données JSON sont dans un fichier appelé 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())
il sera facile d'utiliser csv.DictWriter()
,l'implémentation détaillée peut être comme ceci:
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')
notez que cela suppose que tous vos objets JSON ont les mêmes champs.
voici le référence qui peut vous aider.
j'ai eu des problèmes avec la solution proposée par Dan , mais cela a fonctionné pour moi:
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())
Où ".json " contenait ce qui suit:
[
{"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 } }
]
comme mentionné dans les réponses précédentes, la difficulté dans la conversion de json en csv est parce qu'un fichier json peut contenir des dictionnaires imbriqués et donc être une structure de données multidimensionnelle versus un csv qui est une structure de données 2D. Cependant, un bon moyen de transformer une structure multidimensionnelle csv est d'avoir plusieurs csvs en lien avec les clés primaires.
Dans votre exemple, la première sortie csv a les colonnes "pk","modèle","champs" comme vos colonnes. Valeur pour "pk", et " model "sont faciles à obtenir, mais parce que la colonne" fields "contient un dictionnaire, il devrait être son propre csv et parce que" codename "semble être la clé primaire, vous pouvez utiliser comme entrée pour" fields " pour compléter le premier csv. Le second csv contient le dictionnaire de la colonne "fields" avec le nom de code comme la clé primaire qui peut être utilisé pour lier les 2 csvs ensemble.
Voici une solution pour votre fichier json qui convertit des dictionnaires imbriqués en 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")
je sais que cela fait longtemps que cette question n'a pas été posée, mais j'ai pensé que je pourrais ajouter à la réponse de tout le monde et partager un billet de blog que je pense expliquer la solution d'une manière très concise.
voici le lien
ouvrir un fichier pour écrire
employ_data = open('/tmp/EmployData.csv', 'w')
Créer le fichier csv objet writer
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())
assurez-vous de fermer le fichier pour enregistrer le sommaire
employ_data.close()
Cela fonctionne relativement bien. Il aplatit le json pour l'écrire dans un fichier csv. Les éléments imbriqués sont gérés:)
C'est pour python 3
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)
de profiter de.
Ma façon simple de résoudre ce problème:
crée un nouveau fichier Python comme: json_to_csv.py
ajouter ce code:
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())
après avoir ajouté ce code, sauvegardez le fichier et exécutez sur le terminal:
python json_to_csv.py d'entrée.sortie txt.csv
j'espère que cela vous aidera.
SEEYA!
Modifié Alec McGail la réponse de support de JSON avec les listes de l'intérieur
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
Merci!
ce n'est pas une façon très intelligente de le faire, mais j'ai eu le même problème et cela a fonctionné pour moi:
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()
puisque les données semblent être dans un format de dictionnaire, Il semblerait que vous devriez réellement utiliser csv.DictWriter() réellement sortie les lignes avec les informations d'en-tête. Cela devrait permettre de gérer la conversion un peu plus facilement. Le paramètre fieldnames définirait alors l'ordre correctement tandis que la sortie de la première ligne comme les en-têtes permettrait à celle-ci d'être lue et traitée plus tard par csv.DictReader ().
par exemple, Mike Repass utilisé
output = csv.writer(sys.stdout)
output.writerow(data[0].keys()) # header row
for row in data:
output.writerow(row.values())
toutefois, il suffit de changer la configuration initiale en sortie = csv.DictWriter (filesetting, fieldnames=data[0]).keys())
Note que, depuis l'ordre des éléments dans un dictionnaire n'est pas défini, vous devrez créer fieldnames entrées explicitement. Une fois que vous aurez fait cela, l'écriteurow fonctionnera. Les Écritures fonctionnent alors comme indiqué à l'origine.
malheureusement, je n'ai pas assez de réputation pour apporter une petite contribution à l'étonnante réponse @Alec McGail. J'utilisais Python3 et j'ai eu besoin de convertir la carte en une liste en suivant le commentaire @Alexis R.
de plus, j'ai trouvé que l'auteur csv ajoutait un CR supplémentaire au fichier (j'ai une ligne vide pour chaque ligne avec des données à l'intérieur du fichier csv). La solution a été très facile suite à la réponse de @Jason R. Coombs à ce fil: CSV in Python ajoutant un retour de chariot supplémentaire
vous devez simplement ajouter le paramètre lineterminator='\n' au csv.écrivain. Il sera: csv_w = csv.writer( out_file, lineterminator='\n' )
étonnamment, j'ai trouvé qu'aucune des réponses affichées ici jusqu'à présent ne traite correctement de tous les scénarios possibles (par exemple, dicts imbriqués, listes imbriquées, Aucune valeur, etc.).
Cette solution devrait fonctionner dans tous les scénarios:
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