关键词:
【中文标题】使 Python json 编码器支持 Python 的新数据类【英文标题】:Make the Python json encoder support Python's new dataclasses 【发布时间】:2018-12-19 13:40:23 【问题描述】:从 Python 3.7 开始,有一种叫做数据类的东西:
from dataclasses import dataclass
@dataclass
class Foo:
x: str
但是,以下失败:
>>> import json
>>> foo = Foo(x="bar")
>>> json.dumps(foo)
TypeError: Object of type Foo is not JSON serializable
如何使json.dumps()
将Foo
的实例编码为json objects?
【问题讨论】:
【参考方案1】:就像您可以为 datetime
objects 或小数添加对 JSON 编码器的支持一样,您也可以提供自定义编码器子类来序列化数据类:
import dataclasses, json
class EnhancedJSONEncoder(json.JSONEncoder):
def default(self, o):
if dataclasses.is_dataclass(o):
return dataclasses.asdict(o)
return super().default(o)
json.dumps(foo, cls=EnhancedJSONEncoder)
【讨论】:
重要的是要注意,对于一个名为Foo
的数据类和一个实例 foo_instance = Foo(...)
,dataclasses.is_dataclass(Foo)
和 dataclasses.is_dataclass(foo_instance)
评估为 True
导致 TypeError
用于 @987654330 @ if o
是数据类本身,而不是它的实例。
嵌套怎么样 - 例如序列化d= 'a': Foo(1,2), 'b': Foo(3,4)
你如何用这个处理 ndarray 类型的对象?
使用这样的 json 编码器可以很好地处理嵌套 - json 序列化程序将使用它递归地转换子元素。
至于处理 ndarray,像 if isinstance(obj, np.ndarray) and obj.ndim == 1: return obj.tolist()
这样的东西就可以了。虽然请记住,虽然 python 会愉快地序列化 NaN
或 Infinity
之类的东西,但根据 json 规范,这是不合法的,许多解码器会拒绝它,因此可能建议在编码器中处理这些内容。【参考方案2】:
你不能只使用dataclasses.asdict()
函数来转换数据类吗
听写?比如:
>>> @dataclass
... class Foo:
... a: int
... b: int
...
>>> x = Foo(1,2)
>>> json.dumps(dataclasses.asdict(x))
'"a": 1, "b": 2'
【讨论】:
数据类可能是大型结构的深层嵌套部分。通过使用自定义编码器,您可以将json.dumps("obj": [something_that_may_or_may_not_contain_a_dataclass])
设为
asdict() 将正确处理所有嵌套的数据类,因此,我们得到通常加载到 jison 字符串中的嵌套字典(但是!例如,在加载到字符串之前,必须另外处理 datetime 类型)【参考方案3】:
获取 JSON 化数据类实例的方式
有几个选项可以实现该目标,每个选项的选择都意味着分析哪种方法最适合您的需求:
Standart library: dataclass.asdict
import dataclasses
import json
@dataclass.dataclass
class Foo:
x: str
foo = Foo(x='1')
json_foo = json.dumps(dataclasses.asdict(foo)) # '"x": "1"'
将其取回数据类实例并非易事,因此您可能希望访问该答案https://***.com/a/53498623/2067976
Marshmallow Dataclass
from dataclasses import field
from marshmallow_dataclass import dataclass
@dataclass
class Foo:
x: int = field(metadata="required": True)
foo = Foo(x='1') # Foo(x='1')
json_foo = foo.Schema().dumps(foo) # '"x": "1"'
# Back to class instance.
Foo.Schema().loads(json_foo) # Foo(x=1)
作为marshmallow_dataclass
的奖励,您可以在字段本身上使用验证,当有人使用该模式从 json 反序列化对象时将使用该验证。
Dataclasses Json
from dataclasses import dataclass
from dataclasses_json import dataclass_json
@dataclass_json
@dataclass
class Foo:
x: int
foo = Foo(x='1')
json_foo = foo.to_json() # Foo(x='1')
# Back to class instance
Foo.from_json(json_foo) # Foo(x='1')
此外,除此之外,marshmallow 数据类为您进行了类型转换,而 dataclassses-json(ver.: 0.5.1) 忽略了这一点。
Write Custom Encoder
遵循接受的 Miracle2k 答案并重用自定义 json 编码器。
【讨论】:
感谢marshmallow_dataclass
,它确实是通过验证(甚至是 YAML)从 JSON 获取真实对象的好方法。
@mickours 你欢迎 :) 顺便说一句,我没有提到它也适用于 python36(引擎盖下有 dataclass backport)
c# + Newtonsoft 多年来,dataclasses_json 是最“自然”的——谢谢!
如果您有兴趣,我也会看看dataclass-wizard
。它与dataclasses-json
非常相似,并且效率稍高一些。它还在大多数基本情况下执行类型转换。免责声明:我是这个库的创建者。【参考方案4】:
如果您同意为此使用库,您可以使用dataclasses-json。这是一个例子:
from dataclasses import dataclass
from dataclasses_json import dataclass_json
@dataclass_json
@dataclass
class Foo:
x: str
foo = Foo(x="some-string")
foo_json = foo.to_json()
它还支持嵌入式数据类 - 如果您的数据类有一个类型为另一个数据类的字段 - 如果所有涉及的数据类都具有 @dataclass_json
装饰器。
【讨论】:
我用嵌入式数据类试过这个,但它不起作用【参考方案5】:我建议使用to_json()
方法为您的数据类创建一个父类:
import json
from dataclasses import dataclass, asdict
@dataclass
class Dataclass:
def to_json(self) -> str:
return json.dumps(asdict(self))
@dataclass
class YourDataclass(Dataclass):
a: int
b: int
x = YourDataclass(a=1, b=2)
x.to_json() # '"a": 1, "b": 2'
如果您要向所有数据类添加其他功能,这将特别有用。
【讨论】:
【参考方案6】:使用字典解包可以找到更简单的答案on Reddit
>>> from dataclasses import dataclass
>>> @dataclass
... class MyData:
... prop1: int
... prop2: str
... prop3: int
...
>>> d = 'prop1': 5, 'prop2': 'hi', 'prop3': 100
>>> my_data = MyData(**d)
>>> my_data
MyData(prop1=5, prop2='hi', prop3=100)
【讨论】:
这不支持嵌套数据类。 如果 d 来自 json 对象,这可能会引入错误。我们经常假设在payload中添加一个字段并不是一个破坏性的变化,但是在这里,如果你在d中添加一个字段,它会因为“got an unexpected keyword argument 'prop4'”而中断 就原题而言,这是最简单的正确答案。【参考方案7】:编码dataclass
和SimpleNamespace
对象的最简单方法是向json.dumps()
提供默认函数,该函数会为无法以其他方式序列化的对象调用,并返回对象__dict__
:
json.dumps(foo, default=lambda o: o.__dict__)
【讨论】:
这是一个好主意,通常应该适用于序列化具有简单类型的嵌套模型。我猜这不支持的唯一情况是复杂的 Python 类型,如 Enum 或 datetime,或者在 Union 类型中有数据类的边缘情况,如A | B
。尽管如此,这种方法应该适用于一般的简单案例,如原始问题中所述。【参考方案8】:
您还可以在类中实现asdict
和json.dumps
方法。在这种情况下,无需将 json.dumps
导入项目的其他部分:
from typing import List
from dataclasses import dataclass, asdict, field
from json import dumps
@dataclass
class TestDataClass:
"""
Data Class for TestDataClass
"""
id: int
name: str
tested: bool = False
test_list: List[str] = field(default_factory=list)
@property
def __dict__(self):
"""
get a python dictionary
"""
return asdict(self)
@property
def json(self):
"""
get the json formated string
"""
return dumps(self.__dict__)
test_object_1 = TestDataClass(id=1, name="Hi")
print(test_object_1.__dict__)
print(test_object_1.json)
输出:
'id': 1, 'name': 'Hi', 'tested': False, 'test_list': []
"id": 1, "name": "Hi", "tested": false, "test_list": []
你也可以创建一个父类来继承方法:
from typing import List
from dataclasses import dataclass, asdict, field
from json import dumps
@dataclass
class SuperTestDataClass:
@property
def __dict__(self):
"""
get a python dictionary
"""
return asdict(self)
@property
def json(self):
"""
get the json formated string
"""
return dumps(self.__dict__)
@dataclass
class TestDataClass(SuperTestDataClass):
"""
Data Class for TestDataClass
"""
id: int
name: str
tested: bool = False
test_list: List[str] = field(default_factory=list)
test_object_1 = TestDataClass(id=1, name="Hi")
print(test_object_1.__dict__)
print(test_object_1.json)
【讨论】:
【参考方案9】:好的,这就是我遇到类似情况时所做的。
创建一个自定义字典工厂,将嵌套数据类转换为字典。
def myfactory(数据): return dict(x for x in data if x[1] is not None)
如果 foo 是您的 @dataclass,那么只需提供您的字典工厂以使用“myfactory()”方法:
fooDict = asdict(foo, dict_factory=myfactory)
将 fooDict 转换为 json
fooJson = json.dumps(fooDict)
这应该可以!!
【讨论】:
【参考方案10】:dataclass-wizard 是一个适合您的现代选项。它支持复杂类型,例如日期和时间、typing
模块中的大多数泛型,以及嵌套数据类结构。
PEP 585 和 604 中引入的“新样式”注释可以通过 __future__
导入移植回 Python 3.7,如下所示。
from __future__ import annotations # This can be removed in Python 3.10
from dataclasses import dataclass, field
from dataclass_wizard import JSONWizard
@dataclass
class MyClass(JSONWizard):
my_str: str | None
is_active_tuple: tuple[bool, ...]
list_of_int: list[int] = field(default_factory=list)
string = """
"my_str": 20,
"ListOfInt": ["1", "2", 3],
"isActiveTuple": ["true", false, 1]
"""
instance = MyClass.from_json(string)
print(repr(instance))
# MyClass(my_str='20', is_active_tuple=(True, False, True), list_of_int=[1, 2, 3])
print(instance.to_json())
# '"myStr": "20", "isActiveTuple": [true, false, true], "listOfInt": [1, 2, 3]'
# True
assert instance == MyClass.from_json(instance.to_json())
您可以使用pip
安装数据类向导:
$ pip install dataclass-wizard
一些背景信息:
对于序列化,它使用
dataclasses.asdict
的略微修改(更高效)的实现。在将 JSON 反序列化为数据类实例时,它第一次迭代数据类字段并为每个带注释的类型生成一个解析器,这使得反序列化过程多次运行时效率更高。
免责声明:我是这个库的创建者(和维护者)。
【讨论】:
如何使 JSON 对象可序列化 [重复]
...2013-09-0407:38:37【问题描述】:有没有办法在不使用自定义编码器的情况下序列化python类?我尝试了以下方法,但出现错误:TypeError:helloisnotJSONserializable这很奇怪,因为“hello”是一个字符串。classMyObj(object) 查看详情
使用常规编码器使对象 JSON 可序列化
】使用常规编码器使对象JSON可序列化【英文标题】:MakingobjectJSONserializablewithregularencoder【发布时间】:2013-08-3023:01:12【问题描述】:JSON序列化自定义不可序列化对象的常规方法是继承json.JSONEncoder,然后将自定义编码器传递给js... 查看详情
pytho的基本语法
python3默认的编码格式:utf-8 标识符: 1、可以是数字,字母,下划线组成2、不能以数字开头3、不能以保留字命名(保留字即是关键字,比如"print")4、区分大小写注释:python中单行注释以#开头 实例: #第一个注释pri... 查看详情
覆盖继承的默认支持对象(如 dict、list)的嵌套 JSON 编码
...像它们一样工作。然而,当我想将它们编码为JSON(使用Python)时,我希望它们以一种我可 查看详情
2018年4月10日笔记
Python的编码常见的支持中文的编码有:utf-8,gbk,gb2312常见术语:decode(解码),encode(编码)在Python2中,不写python代码抬头,在print中文字符时会出现乱码;在Python3中不会出现这种情况。因为Python2默认将代码文件内容用ASCII编码处理,... 查看详情
如何使vlc支持fdk-aac编码(windows平台
...通过修改VLC的编译脚本,并加入fdkaac库来使用VLC支持fdkaac编码器。我是在64位的ubuntu16.04下编译的,vlc的编译方法参考VLC官网的编译文档。 1 修改vlc/e 查看详情
python解码(编码)不会使保存的文件饿死(代码片段)
在 Python 中声明编码 [重复]
】在Python中声明编码[重复]【英文标题】:DeclaringencodinginPython[duplicate]【发布时间】:2012-08-2715:02:54【问题描述】:我想使用以下代码在python中拆分一个字符串:means="a،b،c"lst=means.split("،")但我收到此错误消息:语法错误:第2行... 查看详情
如何使python对象json序列化?
】如何使python对象json序列化?【英文标题】:Howtomakeapythonobjectjson-serialized?【发布时间】:2019-07-2301:57:15【问题描述】:我想serializeapythonobject,在将它保存到mysql(基于DjangoORM)后,我想获取它并将这个对象传递给需要这种对象... 查看详情
python标准库之json编码和解码器『详解』(代码片段)
...使用「重点」🧊1、序列化操作2、反序列化操作五、编码器和解码器🥃六、异常🧃参考资料💖相关博客😋一、Pythonjson库介绍JSON 查看详情
python之json编码
...组,可以存放多个对象(5)字符串使用双引号 二、Python类型与json类型转换1 查看详情
如何使 Python 中的 json.dumps 忽略不可序列化的字段
】如何使Python中的json.dumps忽略不可序列化的字段【英文标题】:Howtomakejson.dumpsinPythonignoreanon-serializablefield【发布时间】:2019-01-1110:35:40【问题描述】:我正在尝试使用Construct2.9库对解析一些二进制数据的输出进行序列化。我想... 查看详情
我应该等待 Django 开始支持 Python 3 吗?
】我应该等待Django开始支持Python3吗?【英文标题】:ShouldIwaitforDjangotostartsupportingPython3?【发布时间】:2011-05-1506:53:07【问题描述】:我有一个令我非常兴奋的网站创意,而且我喜欢Python。所以,我对使用Django很感兴趣。不过,... 查看详情
如何使vlc支持fdk-aac编码windows平台
...通过修改VLC的编译脚本,并加入fdkaac库来使用VLC支持fdkaac编码器。我是在64位的ubuntu16.04下编译的,vlc的编译方法参考VLC官网的编译文档。1修改vlc/extras/package/win32/configure.sh,加入--enable-fdkaac,这样VLC就会编译fdkaac这个模块。执行... 查看详情
Python json.loads ValueError,需要分隔符
】Pythonjson.loadsValueError,需要分隔符【英文标题】:Pythonjson.loadsValueError,expectingdelimiter【发布时间】:2015-03-2904:22:22【问题描述】:我将一个postgres表提取为json。输出文件包含如下行:"data":"test":1,"hello":"Ihave\\"!","id":4现在我需要... 查看详情
JSON 字符编码 - 浏览器是不是很好地支持 UTF-8 或者我应该使用数字转义序列?
】JSON字符编码-浏览器是不是很好地支持UTF-8或者我应该使用数字转义序列?【英文标题】:JSONcharacterencoding-isUTF-8well-supportedbybrowsersorshouldIusenumericescapesequences?JSON字符编码-浏览器是否很好地支持UTF-8或者我应该使用数字转义序列... 查看详情
Python 请求返回 JSON 解码器错误
】Python请求返回JSON解码器错误【英文标题】:PythonrequestreturningaJSONDecoderError【发布时间】:2019-09-2519:02:27【问题描述】:我正在尝试从网站中提取数据。我正在使用Python请求:users=requests.get(\'websitenamehere\',headers=headers).json()我收... 查看详情
在 JSON 中编码嵌套的 python 对象
】在JSON中编码嵌套的python对象【英文标题】:EncodingnestedpythonobjectinJSON【发布时间】:2011-07-0619:33:21【问题描述】:我想用JSON编码对象。但是,我不知道如何在没有字符串转义的情况下进行输出。importjsonclassAbc:def__init__(self):self... 查看详情