将JSON数组解析为类实例对象?

8 投票
7 回答
47421 浏览
提问于 2025-04-17 16:40

我正在用Python处理一些数据,我有一些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
        }
    ]
}

如果我想把"metros"这个数组解析成Python类Metro的对象数组,我该如何设置这个类呢?

我在想:

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

所以我想逐个遍历每个地铁,把数据放到对应的Metro对象里,然后把这个对象放到一个Python的对象数组中……我该如何循环遍历JSON里的地铁数据呢?

7 个回答

5

这件事相对简单,因为你已经用 json.load() 读取了数据,这样每个“metros”里的元素就变成了一个Python字典。接下来,只需要遍历这些字典,然后创建 Metro 类的实例就可以了。我对你之前调用 Metro.__init__() 方法的方式做了一些修改,让从 json.load() 返回的字典中传递数据变得更简单。

由于“metros”列表中的每个元素都是一个字典,你可以直接把这个字典传给 Metro 类的构造函数,使用 ** 这种写法可以把字典转换成关键字参数。构造函数就可以通过 update() 方法把这些值更新到自己的 __dict__ 中。

这样做的好处是,和使用 collections.namedtuple 作为简单的数据容器相比,Metro 是一个自定义类,这样你可以很方便地为它添加其他方法或属性。

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

输出:

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

假设你是用json格式来加载数据的,我会在这里使用一个命名元组的列表来存储在' 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)]
>>> 
15

如果你总是得到相同的键值,可以用 ** 这个方式来轻松创建你的实例。如果你只是用 Metro 来存放一些值,把它变成一个 namedtuple 会让你的生活简单很多:

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

撰写回答