web-dev-qa-db-fra.com

Analyser un tableau JSON en objets?

J'essaie d'analyser des données en Python. J'ai du JSON:

{
    "data sources": [
        "http://www.gcmap.com/"
    ],
    "metros": [
        {
            "code": "SCL",
            "continent": "South America",
            "coordinates": {
                "S": 33,
                "W": 71
            },
            "country": "CL",
            "name": "Santiago",
            "population": 6000000,
            "region": 1,
            "timezone": -4
        },
        {
            "code": "LIM",
            "continent": "South America",
            "coordinates": {
                "S": 12,
                "W": 77
            },
            "country": "PE",
            "name": "Lima",
            "population": 9050000,
            "region": 1,
            "timezone": -5
        }
    ]
}

Si je voulais analyser le tableau "metros" dans un tableau d'objets Metro de classe Python, comment pourrais-je configurer la classe?

Je pensais:

class Metro(object):
    def __init__(self):
        self.code = 0
        self.name = ""
        self.country = ""
        self.continent = ""
        self.timezone = ""
        self.coordinates = []
        self.population = 0
        self.region = ""

Je souhaite donc parcourir chaque métro, placer les données dans un objet Metro correspondant et placer cet objet dans un tableau d'objets Python ... Comment puis-je parcourir les métros JSON?

7
thebiglebowski11

Si vous obtenez toujours les mêmes clés, vous pouvez utiliser ** pour construire facilement vos instances. Faire de la Metro une namedtuple vous simplifiera la vie si vous l'utilisez simplement pour conserver des valeurs:

from collections import namedtuple
Metro = namedtuple('Metro', 'code, name, country, continent, timezone, coordinates, population, region')

alors simplement

import json
data = json.loads('''...''')
metros = [Metro(**k) for k in data["metros"]]
14
nneonneo

En supposant que vous utilisez json pour charger les données, je voudrais utiliser une liste de namedtuple ici pour stocker les données sous la clé 'metro'

>>> from collections import namedtuple
>>> metros = []
>>> for e in data[u'metros']:
    metros.append(namedtuple('metro', e.keys())(*e.values()))


>>> metros
[metro(code=u'SCL', name=u'Santiago', country=u'CL', region=1, coordinates={u'S': 33, u'W': 71}, timezone=-4, continent=u'South America', population=6000000), metro(code=u'LIM', name=u'Lima', country=u'PE', region=1, coordinates={u'S': 12, u'W': 77}, timezone=-5, continent=u'South America', population=9050000)]
>>> 
5
Abhijit

Il est relativement facile à faire puisque vous avez lu les données avec json.load() qui, dans ce cas, renverra un dictionnaire Python pour chaque élément dans les "métros" - parcourez-le simplement et créez la liste des instances de Metroclass. J'ai modifié la séquence d'appel de la méthode Metro.__init__() pour faciliter la transmission des données à partir du dictionnaire renvoyé par json.load().

Puisque chaque élément de la liste "métros" du résultat est un dictionnaire, vous pouvez simplement le transmettre au constructeur de la classe Metro en utilisant la notation ** pour le transformer en arguments de mot clé. Le constructeur peut alors simplement update() c'est son propre __dict__ pour transférer ces valeurs sur lui-même. 

En procédant de cette façon, au lieu d'utiliser quelque chose comme un collections.namedtuple comme simple conteneur de données, Metro est une classe personnalisée qui rend l'ajout des autres méthodes et/ou attributs que vous souhaitez trivial.

import json

class Metro(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __str__(self):
        fields = ['    {}={!r}'.format(k,v)
                    for k, v in self.__dict__.items() if not k.startswith('_')]

        return '{}(\n{})'.format(self.__class__.__name__, ',\n'.join(fields))


with open('metros.json') as file:
    json_obj = json.load(file)

metros = [Metro(**metro_dict) for metro_dict in json_obj['metros']]

for metro in metros:
    print('{}\n'.format(metro))

Sortie:

Metro(
    code='SCL',
    continent='South America',
    coordinates={'S': 33, 'W': 71},
    country='CL',
    name='Santiago',
    population=6000000,
    region=1,
    timezone=-4)

Metro(
    code='LIM',
    continent='South America',
    coordinates={'S': 12, 'W': 77},
    country='PE',
    name='Lima',
    population=9050000,
    region=1,
    timezone=-5)
4
martineau

Utilisez le module json de la bibliothèque http://docs.python.org/2/library/json.html pour convertir le json en dictionnaire Python.

2
James Thiele

Peut-être quelque chose comme

import json
data = json.loads(<json string>)
data.metros = [Metro(**m) for m in data.metros]

class Metro(object):
    def __init__(self, **kwargs):
        self.code = kwargs.get('code', 0)
        self.name = kwargs.get('name', "")
        self.county = kwargs.get('county', "")
        self.continent = kwargs.get('continent', "")
        self.timezone = kwargs.get('timezone', "")
        self.coordinates = kwargs.get('coordinates', [])
        self.population = kwargs.get('population', 0)
        self.region = kwargs.get('region', 0)
1
cmd
In [17]: def load_flat(data, inst):
   ....:     for key, value in data.items():
   ....:         if not hasattr(inst, key):
   ....:             raise AttributeError(key)
   ....:         else:
   ....:             setattr(inst, key, value)
   ....:             

In [18]: m = Metro()

In [19]: load_float(data['metros'][0], m)

In [20]: m.__dict__
Out[20]: 
{'code': 'SCL',
 'continent': 'South America',
 'coordinates': {'S': 33, 'W': 71},
 'country': 'CL',
 'name': 'Santiago',
 'population': 6000000,
 'region': 1,
 'timezone': -4}

Non seulement il est très lisible et très explicite à propos de ce qu'il fait, mais il fournit également une validation de base des champs (en soulevant des exceptions sur des champs incompatibles, etc.)

0
Demian Brecht