【问题标题】:deserialize json with objects用对象反序列化 json
【发布时间】:2021-12-17 04:38:15
【问题描述】:

在 python 3 中,如何从 json 反序列化对象结构?

示例 json:

{ 'name': 'foo',
  'some_object': { 'field1': 'bar', 'field2' : '0' },
  'some_list_of_objects': [
      { 'field1': 'bar1', 'field2' : '1' },
      { 'field1': 'bar2', 'field2' : '2' }, 
      { 'field1': 'bar3', 'field2' : '3' },
  ]
}

这是我的python代码:

import json

class A:
  name: str
  some_object: B
  some_list_of_objects: list(C)

  def __init__(self, file_name):
    with open(file_name, "r") as json_file:
        self.__dict__ = json.load(json_file)

class B:
  field1: int
  field2: str

class C:
  field1: int
  field2: str

如何强制some_objectB 类型和some_list_of_objectsC 类型列表?

【问题讨论】:

  • 这些课程是dataclasses吗?
  • 是的,它们可能是数据类
  • 不确定这是否完全符合重复的标准,但可以在here找到一些有趣的答案。
  • 可以说,此时python3 是多余的。 Python 3 是 Python,因为 Python 2.7 大约一年前就已终止使用。
  • @rv.kvetch python-dataclasses 有一个特定的标签。阅读python-3.x的标签说明,看它一般不应该使用,除非是特定情况(特别是不要添加来表明代码运行的版本)

标签: python json


【解决方案1】:

当您使用 Python 3 时,我建议您使用 dataclasses 为您的类建模。例如,这应该会提高您的整体代码质量,并消除为您的类显式声明 __init__ 构造函数方法的需要。

如果您愿意使用第三方库,我建议您研究一个高效的 JSON 序列化库,例如执行隐式类型转换的 dataclass-wizard - 例如,字符串到带注释的 int 如下.请注意,我在这里使用StringIO,这是一个类似文件的对象,其中包含一个 JSON 字符串,用于反序列化为嵌套类模型。

注意:以下方法应该适用于 Python 3.7+。

from __future__ import annotations

from dataclasses import dataclass
from io import StringIO

from dataclass_wizard import JSONWizard


json_data = StringIO("""
{ "name": "foo",
  "some_object": { "field1": "bar", "field2" : "0" },
  "some_list_of_objects": [
      { "field1": "bar1", "field2" : "1" },
      { "field1": "bar2", "field2" : "2" },
      { "field1": "bar3", "field2" : "3" }
  ]
}
""")


@dataclass
class A(JSONWizard):
    name: str
    some_object: B
    some_list_of_objects: list[C]


@dataclass
class B:
    field1: str
    field2: int


@dataclass
class C:
    field1: str
    field2: int


a = A.from_json(json_data.read())

print(f'{a!r}')  # alternatively: print(repr(a))

输出

A(name='foo', some_object=B(field1='bar', field2=0), some_list_of_objects=[C(field1='bar1', field2=1), C(field1='bar2', field2=2), C(field1='bar3', field2=3)])

从 JSON 文件加载

根据this post 中的建议,我不鼓励重写构造方法来传递 JSON 文件的名称以从中加载数据。相反,我建议创建一个如下所示的辅助类方法,如果需要,可以像 A.from_json_file('file.json') 一样调用它。

    @classmethod
    def from_json_file(cls, file_name: str):
        """Deserialize json file contents into an A object."""
        with open(file_name, 'r') as json_file:
            return cls.from_dict(json.load(json_file))

建议

请注意,变量注释(或一般注释)使用方括号 [] 进行下标,而不是上面原始版本中出现的括号。

some_list_of_objects: list(C)

在上述解决方案中,我改为:

some_list_of_objects: list[C]

这是可行的,因为在 PEP 585 中引入了在标准集合中使用下标值。但是,使用 Python 3.7+ 引入的 from __future__ import annotations 语句可以有效地将所有注释转换为 forward-declared 字符串值,因此通常只能在 Python 3.10 中使用的新型注释也可以被移植也适用于 Python 3.7+。

我所做的另一项更改是关于交换声明的类注释的顺序。例如,请注意以下内容:

class B:
  field1: int
  field2: str

但是,请注意 JSON 数据中的相应字段,该字段将被反序列化为 B 对象:

'some_object': { 'field1': 'bar', 'field2' : '0' },

在上面的实现中,我已经在这种情况下换掉了字段注释,所以 B 类被声明为:

class B:
    field1: str
    field2: int

【讨论】:

  • 感谢您的深入了解
  • 没问题!很高兴它有帮助。
猜你喜欢
  • 2015-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多