2014-07-02 6 views
0

Désolé pour le titre vague, j'ai besoin d'aide avec la magie Python et je ne pouvais penser à rien de plus descriptif.Script Python pour convertir des données aplaties compliquées en JSON

J'ai une structure de données JSON fixe dont j'ai besoin pour convertir un fichier CSV. La structure est fixe, mais profondément imbriquée avec des listes et autres. C'est semblable à ceci mais plus compliqué:

{ 
    "foo" : bar, 
    "baz" : qux, 
    "nub" : [ 
     { 
      "bub": "gob", 
      "nab": [ 
       { 
        "nip": "jus", 
        "the": "tip", 
       }, 
       ... 
      ], 
     }, 
     ... 
    ], 
    "cok": "hed" 
} 

Espérons que vous obtenez l'idée. Listes sur dicts sur les listes sur les listes et ainsi de suite. Mon csv pour cela pourrait ressembler à ceci:

foo, baz, nub.bub, nub.nab.nip, nub.nab.the, cok 

bar, qux, "gob" ,,,, "hed" 

,,,,, "nab", "jus","tip",, 

,,,,, "nab", "other", "values",, 

Désolé si cela est difficile à lire, mais l'idée de base est de savoir s'il y a un élément de la liste, il sera dans la ligne suivante, et les valeurs sont répétées pour désigner ce sous -Les listes appartiennent à quoi.

Je ne cherche pas quelqu'un pour trouver une solution à ce bordel, juste peut-être quelques pointeurs sur les techniques ou les choses à examiner.

En ce moment, j'ai un plan sommaire:

Je commence par tourner l'en-tête dans une liste de tuples contenant les clés. Pour chaque groupe de lignes (item), je vais créer une copie de mon modèle dict. J'ai une fonction qui va définir une valeur dict à partir d'un tuple de clés, sauf si elle trouve une liste. Dans ce cas, je vais appeler une fonction récursive funky et passer mon itérateur, et continuer à remplir la dict dans cette fonction, et faire des appels récursifs que je trouve de nouvelles listes.

Je pourrais aussi faire beaucoup de hardcoding, mais qu'est-ce que c'est amusant?

Voilà mon histoire. Encore une fois, juste à la recherche de quelques indications sur la meilleure façon de le faire. Je l'ai écrit rapidement, donc il pourrait être un peu confus, s'il vous plaît laissez-moi savoir si d'autres informations aideraient. Merci!

+0

Les formats en question sont fondamentalement assez distincts que tenter d'analyser un dans l'autre prouvera à la fois problématique et sujette aux erreurs. Pourquoi avez-vous besoin d'un fichier csv comme celui-ci? –

+0

Quelqu'un fait un jeu de données de test pour moi, et c'est le format utilisé. Ce n'est pas idéal mais je ne veux pas les faire changer (ils le font à la main ...). Je peux l'analyser, ça va juste être une douleur dans le cul. – user3779812

Répondre

1

Votre JSON est mal formé. De plus, votre json ne doit pas contenir de tableaux afin de réaliser ce que vous voulez.

def _tocsv(obj, base=''): 
    flat_dict = {} 
    for k in obj: 
     value = obj[k] 
     if isinstance(value, dict): 
      flat_dict.update(_tocsv(value, base + k + '.')) 
     elif isinstance(value, (int, long, str, unicode, float, bool)): 
      flat_dict[base + k] = value 
     else: 
      raise ValueError("Can't serialize value of type "+ type(value).__name__) 
    return flat_dict 

def tocsv(json_content): 
    #assume you imported json 
    value = json.loads(json_content) 
    if isinstance(value, dict): 
     return _tocsv(value) 
    else: 
     raise ValueError("JSON root object must be a hash") 

vous permettra de aplatissez quelque chose comme:

{ 
    foo: "nestor", 
    bar: "kirchner", 
    baz: { 
     clorch: 1, 
     narf: 2, 
     peep: { 
      ooo: "you suck" 
     } 
    } 
} 

en quelque chose comme:

{"foo": "nestor", "bar": "kirchner", "baz.clorch": 1, "baz.narf": 2, "baz.peep.ooo": "you suck"} 

les clés ne permettent pas de préserver un ordre spécifique. vous pouvez remplacer flat_dict = {} par la construction d'un OrderedDict si vous voulez conserver l'ordre.

en supposant que vous avez un tableau de ces dicts plats:

def tocsv_many(json_str): 
    #assume you imported json 
    value = json.loads(json_content) 
    result = [] 
    if isinstance(value, list): 
     for element in value: 
      if isinstance(element, dict): 
       result.append(_tocsv(element)) 
      else: 
       raise ValueError("root children must be dicts") 
    else: 
     raise ValueError("The JSON root must be a list") 
flat_dicts = tocsv_many(yourJsonInput) 

vous pouvez:

  1. créer une liste csvlines = [] qui contiendra les lignes de csv pour ur fichier. Créez un keysSet = set() qui contiendra les clés possibles.
  2. Pour chaque dict que vous avez de cette manière, ajoutez le .keys() à l'ensemble. aucune commande de clé n'est garantie avec un jeu normal; utiliser un ensemble trié à la place. Enfin, nous obtenons la première ligne CSV.

    for flat_dict in flat_dicts: 
        keysSet.extend(flat_dict.keys()) 
    csvlines.appens(",".join(keysSet)) 
    
  3. pour chaque dict que vous avez (itérer à nouveau), vous générer un tableau comme celui-ci:

    for flat_dict in flat_dicts: 
        csvline = ",".join([json.dumps(flat_dict.get(keyInSet, '')) for keyInSet in keysSet]) 
        csvlines.append(csvline) 
    
  4. Voilah! vous avez vos lignes csvlines

+0

Merci pour la réponse! Malheureusement, je vais de csv. Je n'ai aucun contrôle sur les formats de données, c'est juste ce que je dois utiliser. Je vais probablement coder en dur beaucoup de choses pour me sauver un mal de tête. – user3779812

+0

si vous venez de csv c'est encore plus facile. stocker la première ligne et les casser en tant que clés par des virgules. pour chaque ligne suivante: le diviser par des virgules, zip() avec les clés, itérer, et créer une structure json par itération (et en cassant les clés par des points) –

Questions connexes