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
    }
  }
]
121
demandé sur martineau 2009-12-09 07:06:37

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
91
répondu YOU 2016-09-27 12:04:55

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!

67
répondu Alec McGail 2017-04-15 15:28:11

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.

44
répondu vmg 2016-05-18 18:19:22

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!

34
répondu Alex Martelli 2009-12-09 04:27:25

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())
25
répondu Mike Repass 2012-10-05 02:55:35

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())
21
répondu Dan Loewenherz 2013-04-09 18:31:40

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.

13
répondu ReturnHttp402 2016-12-16 06:14:35

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 } }
]
5
répondu Amanda 2017-05-23 12:10:40

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")
4
répondu dmathewwws 2014-02-18 01:19:24

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()
3
répondu user3768804 2017-03-26 12:00:26

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.

2
répondu Loïc 2016-04-24 02:17:23

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!

2
répondu Gabriel Pires 2016-12-13 02:05:02

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!

1
répondu Sawan Vaidya 2016-07-12 11:56:02

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
répondu Juan Luis Martinez 2018-03-05 10:36:36

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.

0
répondu sabbahillel 2014-02-10 14:50:04

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

0
répondu derwyddon 2018-06-29 09:42:51

é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
0
répondu Max Berman 2018-09-18 20:22:33