2013-02-21 14 views
6

Ich versuche, einige Daten in Python ich einige JSON haben zu analysieren:JSON-Array in Objekte analysieren?

{ 
    "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 
     } 
    ] 
} 

Wenn ich die „U-Bahnen“ Array in und Array von Python-Klasse Metro Objekte analysieren wollte, wie würde ich die Klasse Setup?

Ich dachte:

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

Deshalb möchte ich durch jede U-Bahn gehen und die Daten in ein entsprechendes Metro Objekt und eingeführt, die ... von Objekten in einem Python-Array-Objekt Wie kann ich Schleife durch die JSON-Metros?

+0

Ich verstehe die Frage nicht. Wenn Sie JSON haben, haben Sie ein Objekt und Sie können die Metros-Liste von diesem Objekt – voscausa

Antwort

13

Wenn Sie immer die gleichen Schlüssel, Sie ** leicht verwenden können, um Ihre Instanzen aufzubauen. die Metro ein namedtuple Ihr Leben vereinfachen, wenn Sie es einfach verwenden, Werte zu halten:

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

dann einfach

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

+1 für Kwargs Entpacken erhalten. – sberry

+0

das gibt mir einen Fehler: TypeError: string Indizes müssen Ganzzahlen sein – thebiglebowski11

+0

metros = [Metro (** k) für k in Daten ["Metros"]] für diese Zeile – thebiglebowski11

5

Angenommen, Sie json verwenden die Daten zu laden, würde ich ein verwenden hier die Liste von namedtuple die Daten unter dem Schlüssel ‚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)] 
>>> 
+0

+1 für '' namedtuple''. Sie sehen es nicht genug verwendet :) –

+0

Ich würde das 'namedtuple' im Voraus erstellen. 'namedtuple' macht eine' eval' auf einer Klassendefinition, also ist es ziemlich schwer. – nneonneo

-1

speichern ich ast versuchen würde. Etwas wie:

metro = Metro() 
metro.__dict__ = ast.literal_eval(a_single_metro_dict_string) 
+1

JSON ist keine Python-Syntax. Dies wird nicht in allen Fällen funktionieren. – nneonneo

+0

Stimmt, aber aus dem, was ich in der Frage von OP sehe, wird dies genügen. – dmg

+1

Ja ... aber wenn die Frage "Json" sagt, würde ich bei einem JSON-Parser bleiben. – nneonneo

1

Vielleicht so etwas wie

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) 
0
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} 

Nicht nur ist es sehr lesbar und sehr explizit über das, was es tut, aber es bietet auch einige grundlegende Feldvalidierung sowie (Anhebung Ausnahmen bei nicht übereinstimmenden Feldern usw.)

4

Es ist relativ einfach zu tun, da Sie die Daten mit json.load() gelesen haben, die ein Python-Wörterbuch für jedes Element in zurückgibt "Metros" in diesem Fall - gehen Sie einfach durch und erstellen Sie die Liste der Metro Klasseninstanzen. Ich habe die Aufrufreihenfolge der Metro.__init__()-Methode geändert, die es einfacher gemacht hat, Daten aus dem von json.load() zurückgegebenen Wörterbuch an sie zu übergeben.

Da jedes Element der "metros" -Liste im Ergebnis ein Dictionary ist, können Sie es einfach an den Konstruktor der Klasse ** übergeben, um es in Schlüsselwortargumente umzuwandeln. Der Konstruktor kann dann nur update() eigene __dict__, um diese Werte an sich zu übertragen.

Indem Sie die Dinge auf diese Weise tun, anstatt etwas wie collections.namedtuple als nur einen Datencontainer zu verwenden, ist Metro eine benutzerdefinierte Klasse, die das Hinzufügen anderer Methoden und/oder Attribute, die Sie wünschen, trivial macht.

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

Ausgang:

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)