Parsing JSON array into class instance objects?
If I wanted to parse the «metros» array into and array of Python class Metro objects, how would I setup the class? I was thinking:
class Metro(object): def __init__(self): self.code = 0 self.name = "" self.country = "" self.continent = "" self.timezone = "" self.coordinates = [] self.population = 0 self.region = ""
So I want to go through each metro and put the data into a corresponding Metro object and place that object into a Python array of objects. How can I loop through the JSON metros?
I do not understand the question. When you have JSON you have an object and you can get the metros list from this object
7 Answers 7
If you always get the same keys, you can use ** to easily construct your instances. Making the Metro a namedtuple will simplify your life if you are using it simply to hold values:
from collections import namedtuple Metro = namedtuple('Metro', 'code, name, country, continent, timezone, coordinates, population, region')
import json data = json.loads('''. ''') metros = [Metro(**k) for k in data["metros"]]
. did you get data from json.loads ? Because if data is your raw JSON data then of course this won’t work.
. dumps gives you a string. if you already have myData[«metros»] then why are you mucking with json in the first place?
Assuming, you are using json to load the data, I would use a list of namedtuple here to store the data under the key ‘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=, timezone=-4, continent=u'South America', population=6000000), metro(code=u'LIM', name=u'Lima', country=u'PE', region=1, coordinates=, timezone=-5, continent=u'South America', population=9050000)] >>>
I would create the namedtuple in advance. namedtuple does an eval on a class definition, so it is quite heavyweight.
It’s relatively easy to do since you’ve read the data with json.load() which will return a Python dictionary for each element in «metros» in this case — just walk though it and create the list of Metro class instances. I modified the calling sequence of the Metro.__init__() method you had to make it easier to pass data to it from the dictionary returned from json.load() .
Since each element of the «metros» list in the result is a dictionary, you can just pass that to class Metro ‘s constructor using ** notation to turn it into keyword arguments. The constructor can then just update() it’s own __dict__ to transfer those values to itself.
By doing things this way, instead of using something like a collections.namedtuple as just a data container, is that Metro is a custom class which makes adding other methods and/or attributes you wish to it trivial.
import json class Metro(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) def __str__(self): fields = [' <>='.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))
Metro( code='SCL', continent='South America', coordinates=, country='CL', name='Santiago', population=6000000, region=1, timezone=-4) Metro( code='LIM', continent='South America', coordinates=, country='PE', name='Lima', population=9050000, region=1, timezone=-5)
Parse JSON and store data in Python Class
I need to parse the JSON data and instantiate appropriate Classes. The Classes are self-explanatory: i.e. Person has an array of Character classes. How do I instantiate these and store data appropriately? Also, how will I access particular Person data? i.e. Person’s details and characteristics
Note that your JSON example is rather malformed. Apart from the odd quotes you’ve used, you cannot have multiple Character keys in the Charecteristics object either.
I’ve cleaned up your JSON to actually be valid; note that I’ve turned both the group of persons and group of characteristics into lists (as they would normally be).
@Tichodroma: I’ll surely do more research the next time.. Sorry I didnt find the similar question at my first few tries.
2 Answers 2
Take a look at colander; it makes turning a JSON data structure into Python objects dead easy.
import colander class Characteristic(colander.MappingSchema): validator=colander.Range(0, 9999)) name = colander.SchemaNode(colander.String()) rating = colander.SchemaNode(colander.String()) class Characteristics(colander.SequenceSchema): characteristic = Characteristic() class Person(colander.MappingSchema): validator=colander.Range(0, 9999)) name = colander.SchemaNode(colander.String()) phone = colander.SchemaNode(colander.String()) characteristics = Characteristics() class Data(colander.SequenceSchema): person = Person()
then pass in your JSON data structure using the following:
deserialized = Data.deserialize(json.loads(json_string))
JSON to class instance?
If you didn’t need to convert the values from str to int , you could use Point(**json.loads(jsonString)) .
What is the issue, exactly? Where does that string come from? Please see: How to Ask, tour, help center. Also, variable and function names should follow the lower_case_with_underscores style.
4 Answers 4
I was doing json schema validation elsewhere so something simple like this worked for me:
point = Point(**json.loads(jsonString))
You can use a generator expression to consume the appropriate values, by decoding the string then iterating over the values corresponding to x and y .
>>> from operator import itemgetter >>> coords = itemgetter('x', 'y') >>> Point(*(int(x) for x in coords(json.loads(jsonString)))) Point(x=3, y=5)
coords is a function that returns a tuple consisting of its argument’s x and y values. The generator expression ensures each value is converted to an int , and the * syntax unpacks the generator into individual arguments.
A more idiomatic solution, though, would be to define a class method to construct a Point given an appropriate object:
@dataclass class Point: x: int y: int @classmethod def from_dict(cls, d): return cls(d['x'], d['y']) p = Point.from_dict(json.loads(jsonString))
You could also define a from_json class method to wrap from_dict :
@dataclass class Point: x: int y: int @classmethod def from_dict(cls, d): return cls(d['x'], d['y']) @classmethod def from_json(cls, j): return cls.from_dict(json.loads(j)) p = Point.from_json(jsonString)
Though not shown here, the class methods provide places to do validation of the passed JSON string or argument, so you can more gracefully handle things like missing keys, extra keys, JSON values that aren’t objects, etc.