如何将 json 文件转换为 python 类?

Posted

技术标签:

【中文标题】如何将 json 文件转换为 python 类?【英文标题】:How do I convert a json file to a python class? 【发布时间】:2021-12-14 19:17:37 【问题描述】:

考虑这个名为 h.json 的 json 文件,我想将其转换为 python 数据类。


    "acc1":
        "email":"acc1@example.com",
        "password":"acc1",
        "name":"ACC1",
        "salary":1
    ,
    "acc2":
        "email":"acc2@example.com",
        "password":"acc2",
        "name":"ACC2",
        "salary":2
    


我可以使用替代构造函数来获取每个帐户,例如:

import json
from dataclasses import dataclass

@dataclass
class Account(object):
    email:str
    password:str
    name:str
    salary:int
    
    @classmethod
    def from_json(cls, json_key):
        file = json.load(open("h.json"))
        return cls(**file[json_key])

但这仅限于在数据类中定义的参数(电子邮件、姓名等)。

如果我要修改 json 以包含其他内容,比如年龄,该怎么办? 该脚本最终会返回一个TypeError,特别是TypeError: __init__() got an unexpected keyword argument 'age'

有没有办法根据dict(json对象)的key来动态调整class属性,这样就不用每次给json添加新key时都要添加属性了?

【问题讨论】:

为了获得这种灵活性,最好将数据保存为 dict,而不是尝试将其放入类中。 数据类的意义在于它使您无法像这样定义新字段。如果要动态更改可以定义哪些字段,可以使用类。 【参考方案1】:

这样你会失去一些dataclass 功能。

比如判断是不是optional 如自动完成功能

但是,您更熟悉您的项目并做出相应决定

方法肯定有很多,但这是其中之一:

@dataclass
class Account(object):
    email: str
    password: str
    name: str
    salary: int

    @classmethod
    def from_json(cls, json_key):
        file = json.load(open("1.txt"))
        keys = [f.name for f in fields(cls)]
        # or: keys = cls.__dataclass_fields__.keys()
        json_data = file[json_key]
        normal_json_data = key: json_data[key] for key in json_data if key in keys
        anormal_json_data = key: json_data[key] for key in json_data if key not in keys
        tmp = cls(**normal_json_data)
        for anormal_key in anormal_json_data:
            setattr(tmp,anormal_key,anormal_json_data[anormal_key])
        return tmp

test = Account.from_json("acc1")
print(test.age)

【讨论】:

__dataclass_fields__ 属性是数据类模块的内部属性,可以随时更改;您应该更喜欢在此处使用dataclasses.fields已记录)。 @rv.kvetch tank ,但对于这种用法无关紧要 @rv.kvetch 是的,这是真的,坦克? @rv.kvetch 现在你不能在没有密码或电子邮件的情况下创建新的Account 对象,或者......因为这些是必填字段,但age 现在是可选的,例如如果你想更改@ 987654328@ 作为可选字段(意味着:可以在参数中不通过 email 字段的情况下创建新的Account)您必须将第 3 行更改为:email: Optional[str] 是的@Kanishk,对不起【参考方案2】:

由于听起来您的数据可能是动态的,并且您希望在 JSON 对象中添加更多字段而不反映模型中的相同更改,因此我还建议您查看 typing.TypedDict 而不是dataclass.

这是一个 TypedDict 的示例,它应该在 Python 3.7+ 中工作。由于 TypedDict 是 introduced in 3.8,我改为从 typing_extensions 导入它,因此它与 3.7 代码兼容。

from __future__ import annotations

import json
from io import StringIO
from typing_extensions import TypedDict


class Account(TypedDict):
    email: str
    password: str
    name: str
    salary: int


json_data = StringIO("""
    "acc1":
        "email":"acc1@example.com",
        "password":"acc1",
        "name":"ACC1",
        "salary":1
    ,
    "acc2":
        "email":"acc2@example.com",
        "password":"acc2",
        "name":"ACC2",
        "salary":2,
        "someRandomKey": "string"
    

""")

data = json.load(json_data)
name_to_account: dict[str, Account] = data

acct = name_to_account['acc2']

# Your IDE should be able to offer auto-complete suggestions within the
# brackets, when you start typing or press 'Ctrl + Space' for example.
print(acct['someRandomKey'])

如果您开始使用数据类对数据建模,我建议您查看像 dataclass-wizard 这样的 JSON 序列化库,它应该处理 JSON 数据中的无关字段,如如果您发现数据变得更加复杂,还可以使用嵌套数据类模型。

它还有一个方便的工具,您可以使用该工具从 JSON 数据生成数据类架构,例如,如果您想在上述 JSON 文件中添加新字段时更新您的模型类,该工具非常有用。

【讨论】:

哇,TypedDict !!!好主意 是的,绝对同意,这很酷,但我觉得typing 的一个不太知名的功能:-) @rv.kvetch,这肯定会派上用场,感谢您让我知道,但是对于我的特定用例,除了 alt 构造函数之外,Account 类中还有许多其他方法,并从TypedDict 继承限制为仅在类中使用注释,而且我没有得到“someRandomKey”的类型提示,因为我没有在类中指定该字段。感谢您让我知道这一点。 啊,这绝对是有道理的。是的,同意,TypedDict 的一个限制是您不能像往常一样定义和使用方法。如果您仍然仍然使用数据类,我建议您查看上面的链接库,因为它有一个 CLI 工具,您可以使用它来将 JSON 模式转换为数据类模型,这可能会被使用如果你添加了一堆新的 JSON 字段。实际上,它的部分灵感来自这里的另一个优秀工具:russbiggs.github.io/json2dataclass【参考方案3】:

对于平面(非嵌套数据类),下面的代码可以完成这项工作。 如果您需要处理嵌套的数据类,您应该使用像 dacite 这样的框架。注意 1 从 json 文件加载数据不应该是您的类逻辑的一部分。

注意 2 如果您的 json 可以包含任何内容 - 您不能将其映射到数据类,您应该使用 dict

from dataclasses import dataclass
from typing import List

data = 
    "acc1":
        "email":"acc1@example.com",
        "password":"acc1",
        "name":"ACC1",
        "salary":1
    ,
    "acc2":
        "email":"acc2@example.com",
        "password":"acc2",
        "name":"ACC2",
        "salary":2
    





@dataclass
class Account:
    email:str
    password:str
    name:str
    salary:int

accounts: List[Account] = [Account(**x) for x in data.values()]
print(accounts)

输出

[Account(email='acc1@example.com', password='acc1', name='ACC1', salary=1), Account(email='acc2@example.com', password='acc2', name='ACC2', salary=2)]

【讨论】:

我打算对 obj.__dict__.update(x) 做同样的事情,但这样更好。

以上是关于如何将 json 文件转换为 python 类?的主要内容,如果未能解决你的问题,请参考以下文章

python中json文件如何转换为外部链接

如何将 XML/JSON 文件转换为 C# 类?

使用 attrs 将 JSON 转换为 Python 类

将 JSON 模式转换为 python 类

在Python 中如何将类对象序列化为JSON?

如何在 Python 中将类对象转换为 JSON