web-dev-qa-db-fra.com

Comment puis-je convertir JSON au format CSV?

J'ai un fichier JSON que je veux convertir en fichier CSV. Comment puis-je faire cela 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, cela n'a pas fonctionné. J'utilise Django et l'erreur que j'ai reçue est la suivante:

file' object has no attribute 'writerow'

Alors, j'ai essayé ce qui suit:

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

Je reçois 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
    }
  }
]
131
little_fish

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, il ne peut donc normalement pas être directement converti au format CSV .

{
    "pk": 22,
    "model": "auth.permission",
    "codename": "add_logentry",
    "content_type": 8,
    "name": "Can add log entry"
},
......]

Voici mon code pour générer du 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 une sortie en tant que:

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
100
YOU

Je suppose que votre fichier JSON sera décodé dans une liste de dictionnaires. Nous avons d’abord besoin d’une fonction qui aplatit 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

Résultat de l'exécution de cet extrait de code 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ée d'objets JSON:

input = map( lambda x: flattenjson( x, "__" ), input )

et trouver les noms de colonne appropriés:

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 ça aide!

76
Alec McGail

Avec pandasbibliothèque , , c'est aussi simple que d'utiliser deux commandes!

pandas.read_json()

Pour convertir une chaîne JSON en un objet pandas (une série ou un cadre de données). Ensuite, en supposant que les résultats ont été stockés sous la forme df:

df.to_csv()

Ce qui peut soit renvoyer une chaîne, soit écrire directement dans un fichier csv.

Sur la base de la verbosité des réponses précédentes, nous devrions tous remercier les pandas pour le raccourci.

57
vmg

JSON peut représenter une grande variété de structures de données: un "objet" JS ressemble à peu près à un dict Python (avec des clés de chaîne), un "tableau" JS ressemble à une liste Python, et vous pouvez les imbriquer aussi longtemps que le dernier. "feuille" sont des nombres ou des chaînes.

CSV peut essentiellement ne représenter qu’une table à deux dimensions - éventuellement avec une première rangée d’en-têtes, c’est-à-dire des "noms de colonne", qui peuvent rendre la table interprétable en tant que liste de noms, au lieu de l’interprétation normale, liste de listes (encore une fois, les éléments "feuille" peuvent être des nombres ou des chaînes).

Ainsi, dans le cas général, vous ne pouvez pas traduire une structure JSON arbitraire en un fichier CSV. Dans quelques cas spéciaux, vous pouvez (tableau de tableaux sans autre imbrication; tableaux d'objets qui ont tous exactement les mêmes clés). Quel cas particulier, le cas échéant, s'applique à votre problème? Les détails de la solution dépendent de votre cas particulier. Étant donné le fait étonnant que vous ne mentionnez même pas celle qui s'applique, je suppose que vous n'avez peut-être pas pris en compte la contrainte, aucun cas utilisable ne s'applique en fait et votre problème est impossible à résoudre. Mais s'il vous plaît clarifiez!

34
Alex Martelli

Une solution générique qui traduit toute liste json de flat objects en csv.

Passez le fichier input.json en tant que premier argument sur la 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())
25
Mike Repass

Ce code devrait fonctionner pour vous, en supposant que vos données JSON se trouvent 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())
21
Dan Loewenherz

Il sera facile d’utiliser csv.DictWriter(), l’implémentation détaillée peut ressembler à ceci:

def read_json(filename):
    return json.loads(open(filename).read())
def write_csv(data,filename):
    with open(filename, 'w+') 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 la référence qui peut vous aider.

15
ReturnHttp402

J'avais 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ù "test.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 } }
]
6
Amanda

Comme mentionné dans les réponses précédentes, la difficulté de convertir json en csv est due au fait qu'un fichier json peut contenir des dictionnaires imbriqués et donc constituer une structure de données multidimensionnelle par rapport à un csv qui est une structure de données 2D. Cependant, un bon moyen de transformer une structure multidimensionnelle en un fichier csv consiste à avoir plusieurs csv liés aux clés primaires.

Dans votre exemple, la première sortie csv a les colonnes "pk", "modèle", "champs" comme colonnes. Les valeurs pour "pk" et "model" sont faciles à obtenir, mais comme la colonne "fields" contient un dictionnaire, il doit s'agir de son propre csv et, comme "codename" apparaît comme étant la clé primaire, vous pouvez l'utiliser comme entrée. pour "champs" pour compléter le premier csv. Le second csv contient le dictionnaire de la colonne "champs" avec le nom de code comme clé primaire pouvant être utilisée pour lier les 2 csv.

Voici une solution pour votre fichier json qui convertit un dictionnaire imbriqué en 2 csv.

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")
4
dmathewwws

Je sais que cela fait longtemps que cette question n'a pas été posée, mais je pensais pouvoir ajouter à la réponse de tous les autres et partager un article de blog qui, je pense, explique la solution de manière très concise.

Voici le lien

Ouvrir un fichier pour l'écriture

employ_data = open('/tmp/EmployData.csv', 'w')

Créer l'objet écrivain 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())

Assurez-vous de fermer le fichier afin de sauvegarder le contenu

employ_data.close()
4
user3768804

Mon moyen simple de résoudre ceci:

Créez un nouveau fichier Python comme: json_to_csv.py

Ajoutez 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, enregistrez le fichier et exécutez-le sur le terminal:

python json_to_csv.py input.txt output.csv

J'espère que cela vous aidera.

À PLUS!

2
Gabriel Pires

Cela fonctionne relativement bien ... Cela aplatit le JSON pour l'écrire dans un fichier csv ... "

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)

prendre plaisir.

2
Loïc
import json,csv
t=''
t=(type('a'))
json_data = []
data = None
write_header = True
item_keys = []
try:
with open('kk.json') as json_file:
    json_data = json_file.read()

    data = json.loads(json_data)
except Exception as e:
    print( e)

with open('bar.csv', 'at') as csv_file:
    writer = csv.writer(csv_file)#, quoting=csv.QUOTE_MINIMAL)
    for item in data:
        item_values = []
        for key in item:
            if write_header:
                item_keys.append(key)
            value = item.get(key, '')
            if (type(value)==t):
                item_values.append(value.encode('utf-8'))
            else:
                item_values.append(value)
        if write_header:
            writer.writerow(item_keys)
            write_header = False
        writer.writerow(item_values)

Ce code fonctionne pour n'importe quel fichier JSON

# -*- coding: utf-8 -*-
"""
Created on Mon Jun 17 20:35:35 2019
author: Ram
"""

import json
import csv

with open("file1.json") as file:
    data = json.load(file)



# create the csv writer object
pt_data1 = open('pt_data1.csv', 'w')
csvwriter = csv.writer(pt_data1)

count = 0

for pt in data:

      if count == 0:

             header = pt.keys()

             csvwriter.writerow(header)

             count += 1

      csvwriter.writerow(pt.values())

pt_data1.close()
1
Ram Prajapati

La réponse d'Alec est excellent, mais cela ne fonctionne pas dans le cas où il y a plusieurs niveaux d'imbrication. Voici une version modifiée qui prend en charge plusieurs niveaux d'imbrication. Cela rend également les noms d'en-tête un peu plus agréables si l'objet imbriqué spécifie déjà sa propre clé (par exemple, les données Firebase Analytics/BigTable/BigQuery):

"""Converts JSON with nested fields into a flattened CSV file.
"""

import sys
import json
import csv
import os

import jsonlines

from orderedset import OrderedSet

# from https://stackoverflow.com/a/28246154/473201
def flattenjson( b, prefix='', delim='/', val=None ):
  if val == None:
    val = {}

  if isinstance( b, dict ):
    for j in b.keys():
      flattenjson(b[j], prefix + delim + j, delim, val)
  Elif isinstance( b, list ):
    get = b
    for j in range(len(get)):
      key = str(j)

      # If the nested data contains its own key, use that as the header instead.
      if isinstance( get[j], dict ):
        if 'key' in get[j]:
          key = get[j]['key']

      flattenjson(get[j], prefix + delim + key, delim, val)
  else:
    val[prefix] = b

  return val

def main(argv):
  if len(argv) < 2:
    raise Error('Please specify a JSON file to parse')

  filename = argv[1]
  allRows = []
  fieldnames = OrderedSet()
  with jsonlines.open(filename) as reader:
    for obj in reader:
      #print obj
      flattened = flattenjson(obj)
      #print 'keys: %s' % flattened.keys()
      fieldnames.update(flattened.keys())
      allRows.append(flattened)

  outfilename = filename + '.csv'
  with open(outfilename, 'w') as file:
    csvwriter = csv.DictWriter(file, fieldnames=fieldnames)
    csvwriter.writeheader()
    for obj in allRows:
      csvwriter.writerow(obj)



if __== '__main__':
  main(sys.argv)
1
phreakhead

Essaye ça

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 item in data:
    output.writerow(item.values())
1

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()
1
Juan Luis Martinez

Modification de la réponse d'Alec McGail pour prendre en charge JSON avec des listes à 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!

1
Sawan Vaidya

Vous pouvez utiliser ce code pour convertir un fichier JSON en fichier csv Après avoir lu le fichier, je convertis l'objet en pandas dataframe, puis je l'enregistre dans un fichier CSV.

import os
import pandas as pd
import json
import numpy as np

data = []
os.chdir('D:\\Your_directory\\folder')
with open('file_name.json', encoding="utf8") as data_file:    
     for line in data_file:
        data.append(json.loads(line))

dataframe = pd.DataFrame(data)        
## Saving the dataframe to a csv file
dataframe.to_csv("filename.csv", encoding='utf-8',index= False)
0
Terminator17

De manière surprenante, j’ai trouvé qu’aucune des réponses postées ici jusqu’à présent ne traitait correctement de tous les scénarios possibles (par exemple, dict imbriqués, listes imbriquées, valeurs None, 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
0
Max Berman

Puisque les données semblent être dans un format de dictionnaire, il semblerait que vous devriez réellement utiliser csv.DictWriter () pour générer les lignes avec les informations d'en-tête appropriées. Cela devrait permettre de traiter la conversion un peu plus facilement. Le paramètre noms de champs définirait alors correctement l'ordre tandis que la sortie de la première ligne sous forme d'en-têtes lui permettrait d'être lue et traitée ultérieurement 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())

Cependant, changez simplement la configuration initiale en output = csv.DictWriter (définition de fichier, noms de champ = data [0] .keys ())

Notez que, l'ordre des éléments dans un dictionnaire n'étant pas défini, vous devrez peut-être créer des entrées de noms de champs explicitement. Une fois que vous faites cela, le writerow travaillera. Les écritures fonctionnent ensuite comme indiqué à l'origine.

0
sabbahillel

Malheureusement, je n'ai pas assez de réputation pour apporter une petite contribution à la réponse incroyable de @Alec McGail . J'utilisais Python3 et j'avais besoin de convertir la carte en liste après le commentaire @Alexis R. 

De plus, j'ai trouvé que l'auteur de csv ajoutait un CR supplémentaire au fichier (j'ai une ligne vide pour chaque ligne contenant des données à l'intérieur du fichier csv). La solution était très facile après la réponse @Jason R. Coombs à ce fil de discussion: CSV en Python en ajoutant un retour chariot supplémentaire

Vous devez simplement ajouter le paramètre lineterminator = '\ n' au fichier csv.writer. Ce sera: csv_w = csv.writer( out_file, lineterminator='\n' )

0
derwyddon