【问题标题】:Parsing JSON array into class instance objects?将 JSON 数组解析为类实例对象?
【发布时间】:2013-02-21 19:14:35
【问题描述】:

我正在尝试在 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 并将数据放入相应的 Metro 对象中,并将该对象放入 Python 对象数组中...如何循环遍历 JSON Metro?

【问题讨论】:

  • 我不明白这个问题。当你有 JSON 时,你有一个对象,你可以从这个对象中获取 Metros 列表

标签: python json


【解决方案1】:

这相对容易做到,因为您已经使用json.load() 读取了数据,在这种情况下,它将为“metros”中的每个元素返回一个 Python 字典——只需遍历它并创建Metroclass 实例的列表.我修改了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)

【讨论】:

    【解决方案2】:

    如果您总是获得相同的密钥,您可以使用** 轻松构建您的实例。将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"]]
    

    【讨论】:

    • 这给了我一个错误:TypeError: string indices must be integers
    • metros = [Metro(**k) for k in data["metros"]] 为那条线
    • ...你有没有从json.loads 得到data?因为如果 data 是您的原始 JSON 数据,那么这当然行不通。
    • 我使用了 metros = json.dumps[myData["metros"]] 然后 myMetros = [Metro(**k) for k in metros["metros"]]
    • ...??? dumps 给你一个字符串......如果你已经有 myData["metros"] 那么你为什么要与 json 混在一起呢?
    【解决方案3】:

    假设你使用 json 来加载数据,我会在这里使用一个 namedtuple 列表来存储键 '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)]
    >>> 
    

    【讨论】:

    • 我会提前创建namedtuplenamedtuple 在类定义上做了一个eval,所以它是相当重量级的。
    【解决方案4】:

    使用库 http://docs.python.org/2/library/json.html 中的 json 模块将 json 转换为 Python 字典

    【讨论】:

      【解决方案5】:

      可能是这样的

      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)
      

      【讨论】:

        【解决方案6】:
        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}
        

        它不仅可读性强并且对它的作用非常明确,而且还提供了一些基本的字段验证(在不匹配的字段上引发异常等)

        【讨论】:

          【解决方案7】:

          我会尝试ast。比如:

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

          【讨论】:

          • 没错,但从我在 OP 的问题中看到的情况来看,这就足够了。
          • 是的......但如果问题是“json”,我会坚持使用 JSON 解析器。
          • 如果“正确”是一个比较形容词,我会说你的答案是“更正确”
          猜你喜欢
          • 2016-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-22
          • 2020-05-02
          • 1970-01-01
          • 2019-01-18
          • 1970-01-01
          相关资源
          最近更新 更多