该库提供了一个简单的API,用于将dataclasses编码和解码为JSON格式。
入门非常简单。
README / 文档网站。具有导航栏和搜索功能,应该与此README完全一致 -- 快来看看吧!
pip install dataclasses-json
from dataclasses import dataclass from dataclasses_json import dataclass_json @dataclass_json @dataclass class Person: name: str person = Person(name='lidatong') person.to_json() # '{"name": "lidatong"}' <- 这是一个字符串 person.to_dict() # {'name': 'lidatong'} <- 这是一个字典 Person.from_json('{"name": "lidatong"}') # Person(1) Person.from_dict({'name': 'lidatong'}) # Person(1) # 你还可以使用另一种API来应用_模式验证_ # 这对于"类型化"的Python代码很有用 Person.from_json('{"name": 42}') # 这是可以的。42不是`str`类型,但 # dataclass创建不验证类型 Person.schema().loads('{"name": 42}') # 错误!引发`ValidationError`
如果你想使用驼峰命名法的JSON怎么办?
# 与上面相同的导入,另外导入`LetterCase` from dataclasses import dataclass from dataclasses_json import dataclass_json, LetterCase @dataclass_json(letter_case=LetterCase.CAMEL) # 现在所有字段都以驼峰命名法编码/解码 @dataclass class ConfiguredSimpleExample: int_field: int ConfiguredSimpleExample(1).to_json() # {"intField": 1} ConfiguredSimpleExample.from_json('{"intField": 1}') # ConfiguredSimpleExample(1)
它是递归的(见下面的注意事项),所以你可以轻松处理嵌套的数据类。 除了py to JSON 表格中支持的类型外,这个库还支持以下类型:
支持任意的Collection类型。
Mapping类型被编码为JSON对象,str
类型被编码为JSON字符串。
其他所有Collection类型都被编码为JSON数组,但解码时会还原为原始的集合类型。
datetime对象。datetime
对象使用timestamp被编码为float
(JSON数字)。
如datetime
文档中所述,如果你的datetime
对象是naive的,调用.timestamp()
时会假定使用你的系统本地时区。在你的数据类中对应于datetime
字段的JSON数字会被解码为datetime-aware对象,tzinfo
设置为你的系统本地时区。
因此,如果你编码一个datetime-naive对象,你将解码得到一个datetime-aware对象。这很重要,因为编码和解码不会严格地互为逆操作。如果你想覆盖这个默认行为(例如,如果你想使用ISO),请参阅这一节。
UUID对象。它们被编码为str
(JSON字符串)。
Decimal对象。它们也被编码为str
。
最新版本同时兼容Python 3.7和Python 3.6(使用dataclasses向后移植)。
from dataclasses import dataclass from dataclasses_json import dataclass_json @dataclass_json @dataclass class Person: name: str lidatong = Person('lidatong') # 编码为JSON lidatong.to_json() # '{"name": "lidatong"}' # 从JSON解码 Person.from_json('{"name": "lidatong"}') # Person(name='lidatong')
注意,@dataclass_json
装饰器必须堆叠在@dataclass
装饰器之上(顺序很重要!)
from dataclasses import dataclass from dataclasses_json import DataClassJsonMixin @dataclass class Person(DataClassJsonMixin): name: str lidatong = Person('lidatong') # 与上面的方法1不同的例子,但使用方法完全相同 assert Person.from_json(lidatong.to_json()) == lidatong
选择适合你口味的方法。注意,在使用_静态分析_工具(如代码检查、类型检查)时,混入方法有更好的支持,但在_运行时_使用中,这些实现上的差异是不可见的。
from dataclasses import dataclass from dataclasses_json import dataclass_json @dataclass_json @dataclass class Person: name: str
将我的数据类实例编码为JSON数组
people_json = [Person('lidatong')] Person.schema().dumps(people_json, many=True) # '[{"name": "lidatong"}]'
将包含我的数据类实例的JSON数组解码
people_json = '[{"name": "lidatong"}]' Person.schema().loads(people_json, many=True) # [Person(name='lidatong')]
将我的数据类编码为更大的JSON对象的一部分(例如HTTP请求/响应)
import json response_dict = { 'response': { 'person': Person('lidatong').to_dict() } } response_json = json.dumps(response_dict)
在这种情况下,我们分两步进行。首先,我们使用.to_dict
将数据类编码为Python字典而不是JSON字符串。
其次,我们利用内置的json.dumps
将我们的dataclass
序列化为JSON字符串。
将包含我的数据类的更大JSON对象解码(例如HTTP响应)
import json response_dict = json.loads('{"response": {"person": {"name": "lidatong"}}}') person_dict = response_dict['response'] person = Person.from_dict(person_dict)
与上面的编码类似,我们利用内置的json
模块。
首先,调用json.loads
将整个JSON对象读入字典。然后我们访问包含我们想要解码的Person
编码字典的值的键(response_dict['response']
)。
其次,我们使用Person.from_dict
加载该字典。
这可以通过调用.schema()
然后使用相应的编码器/解码器方法来实现,即.load(...)
/.dump(...)
。
编码为单个Python字典
person = Person('lidatong') person.to_dict() # {'name': 'lidatong'}
编码为Python字典列表
people = [Person('lidatong')] Person.schema().dump(people, many=True) # [{'name': 'lidatong'}]
将字典解码为单个数据类实例
person_dict = {'name': 'lidatong'} Person.from_dict(person_dict) # Person(name='lidatong')
将字典列表解码为数据类实例列表
people_dicts = [{"name": "lidatong"}] Person.schema().load(people_dicts, many=True) # [Person(name='lidatong')]
按照惯例,JSON使用驼峰命名法,而Python成员按惯例使用蛇形命名法。
你可以在类级别和字段级别配置它以从其他命名方案编码/解码。
from dataclasses import dataclass, field from dataclasses_json import LetterCase, config, dataclass_json # 在类级别更改命名方案 @dataclass_json(letter_case=LetterCase.CAMEL) @dataclass class Person: given_name: str family_name: str Person('Alice', 'Liddell').to_json() # '{"givenName": "Alice"}' Person.from_json('{"givenName": "Alice", "familyName": "Liddell"}') # Person('Alice', 'Liddell') # 在字段级别 @dataclass_json @dataclass class Person: given_name: str = field(metadata=config(letter_case=LetterCase.CAMEL)) family_name: str Person('Alice', 'Liddell').to_json() # '{"givenName": "Alice"}' # 注意`family_name`字段仍然是蛇形命名法,因为上面没有配置它 Person.from_json('{"givenName": "Alice", "family_name": "Liddell"}') # Person('Alice', 'Liddell')
本库假设你的字段遵循Python的蛇形命名法命名约定。
如果你的字段一开始就不是snake_case
,而你试图参数化LetterCase
,
那么编码/解码的行为是未定义的(很可能会导致微妙的错误)。
from dataclasses import dataclass, field from dataclasses_json import config, dataclass_json @dataclass_json @dataclass class Person: given_name: str = field(metadata=config(field_name="overriddenGivenName")) Person(given_name="Alice") # Person('Alice') Person.from_json('{"overriddenGivenName": "Alice"}') # Person('Alice') Person('Alice').to_json() # {"overriddenGivenName": "Alice"}
默认情况下,如果你的数据类中使用了default
或default_factory
的任何字段,在解码时,如果JSON中缺少相应的字段,这些字段将使用提供的默认值填充。
解码缺少字段的JSON
@dataclass_json @dataclass class Student: id: int name: str = 'student' Student.from_json('{"id": 1}') # Student(id=1, name='student')
注意from_json
在JSON中缺少name
字段时,用指定的默认值'student'填充了该字段。
有时你可能有一些类型为Optional
的字段,但你不一定想为它们指定默认值。在这种情况下,你可以使用infer_missing
关键字参数,使from_json
将缺失的字段值推断为None
。
解码没有默认值的可选字段
@dataclass_json @dataclass class Tutor: id: int student: Optional[Student] = None Tutor.from_json('{"id": 1}') # Tutor(id=1, student=None)
个人建议你利用数据类的默认值而不是使用infer_missing
,但如果出于某种原因你需要将JSON解码行为与字段的默认值解耦,这种方法可以让你做到这一点。
默认情况下,当json_dataclass
接收到未定义的输入参数时,具体行为取决于实现方式。
(from_dict
方法会忽略它们,而使用schema()
加载时会引发ValidationError。)
有三种方法可以自定义这种行为。
假设你想用以下字典实例化一个数据类:
dump_dict = {"endpoint": "some_api_endpoint", "data": {"foo": 1, "bar": "2"}, "undefined_field_name": [1, 2, 3]}
undefined
关键字设置为Undefined.RAISE
来强制始终引发错误
(不区分大小写的字符串'RAISE'也可以)。当然,如果你不传入任何未定义的参数,它会正常工作。from dataclasses_json import Undefined @dataclass_json(undefined=Undefined.RAISE) @dataclass() class ExactAPIDump: endpoint: str data: Dict[str, Any] dump = ExactAPIDump.from_dict(dump_dict) # 引发UndefinedParameterError
undefined
关键字设置为Undefined.EXCLUDE
来简单地忽略任何未定义的参数
(不区分大小写的字符串'EXCLUDE'也可以)。注意,你将无法使用to_dict
检索它们:from dataclasses_json import Undefined @dataclass_json(undefined=Undefined.EXCLUDE) @dataclass() class DontCareAPIDump: endpoint: str data: Dict[str, Any] dump = DontCareAPIDump.from_dict(dump_dict) # DontCareAPIDump(endpoint='some_api_endpoint', data={'foo': 1, 'bar': '2'}) dump.to_dict() # {"endpoint": "some_api_endpoint", "data": {"foo": 1, "bar": "2"}}
undefined
关键字设置为Undefined.INCLUDE
(不区分大小写的字符串'INCLUDE'也可以),并定义一个类型为CatchAll
的字段,所有未知值都将存储在其中。
这只是一个可以容纳任何内容的字典。
如果没有未定义的参数,这将是一个空字典。from dataclasses_json import Undefined, CatchAll @dataclass_json(undefined=Undefined.INCLUDE) @dataclass() class UnknownAPIDump: endpoint: str data: Dict[str, Any] unknown_things: CatchAll dump = UnknownAPIDump.from_dict(dump_dict) # UnknownAPIDump(endpoint='some_api_endpoint', data={'foo': 1, 'bar': '2'}, unknown_things={'undefined_field_name': [1, 2, 3]}) dump.to_dict() # {'endpoint': 'some_api_endpoint', 'data': {'foo': 1, 'bar': '2'}, 'undefined_field_name': [1, 2, 3]}
注意:
Undefined.INCLUDE
时,如果你没有指定恰好一个CatchAll
类型的字段,将会引发UndefinedParameterError
。LetterCase
不会影响写入CatchAll
字段的值,它们将保持原样。CatchAll
字段指定默认值(或默认工厂)时,例如unknown_things: CatchAll = None
,如果没有未定义的参数,将使用默认值而不是空字典。这3个选项也适用于使用schema().loads
和schema().dumps
,只要你不通过指定schema(unknown=<marshmallow值>)
来覆盖它。
marshmallow使用相同的3个关键字['include', 'exclude', 'raise']。
这3个操作也适用于使用__init__
,例如UnknownAPIDump(**dump_dict)
将不会引发TypeError
,而是将所有未知值写入标记为CatchAll
的字段。
标记为EXCLUDE
的类也会简单地忽略未知参数。请注意,标记为RAISE
的类如果提供了未知关键字,仍会引发TypeError
,而不是UndefinedParameterError
。
参见覆盖
字段类型与声明它们的类型相同的对象层次结构需要一个小技巧来声明前向引用。
from typing import Optional from dataclasses import dataclass from dataclasses_json import dataclass_json @dataclass_json @dataclass class Tree(): value: str left: Optional['Tree'] right: Optional['Tree']
避免使用
from __future__ import annotations
因为它会导致dataclasses_json访问类型注解的方式出现问题。
数据分析和机器学习中常用库如numpy和pandas的特定数据类型默认不支持,但你可以通过使用自定义解码器和编码器轻松启用它们。以下是numpy和pandas类型的两个示例。
from dataclasses import field, dataclass from dataclasses_json import config, dataclass_json import numpy as np import pandas as pd @dataclass_json @dataclass class DataWithNumpy: my_int: np.int64 = field(metadata=config(decoder=np.int64)) my_float: np.float64 = field(metadata=config(decoder=np.float64)) my_array: np.ndarray = field(metadata=config(decoder=np.asarray)) DataWithNumpy.from_json("{\"my_int\": 42, \"my_float\": 13.37, \"my_array\": [1,2,3]}") @dataclass_json @dataclass class DataWithPandas: my_df: pd.DataFrame = field(metadata=config(decoder=pd.DataFrame.from_records, encoder=lambda x: x.to_dict(orient="records"))) data = DataWithPandas.from_dict({"my_df": [{"col1": 1, "col2": 2}, {"col1": 3, "col2": 4}]}) # my_df 结果为: # col1 col2 # 1 2 # 3 4 data.to_dict() # {"my_df": [{"col1": 1, "col2": 2}, {"col1": 3, "col2": 4}]}
使用dataclass_json
装饰器或混入DataClassJsonMixin
将为你提供一个额外的方法.schema()
。
.schema()
生成的模式与手动为你的数据类创建marshmallow模式完全等价。你可以参考marshmallow API文档
了解使用.schema()
返回的模式的其他方法。
你可以向.schema()
传递与构造PersonSchema
实例时完全相同的参数,例如.schema(many=True)
,它们将被传递给marshmallow模式。
from dataclasses import dataclass from dataclasses_json import dataclass_json @dataclass_json @dataclass class Person: name: str # 你不需要这样做 - `.schema()`会为你生成这个! from marshmallow import Schema, fields class PersonSchema(Schema): name = fields.Str()
简要说明上述示例中的内部工作原理:调用.schema()
将使本库为你生成一个
marshmallow模式。
它还填充了相应的对象钩子,以便marshmallow在load
时创建你的数据类的实例(例如
Person.schema().load
返回一个Person
),而不是默认的dict
。
性能说明
.schema()
没有缓存(它在每次调用时生成模式),所以如果你有一个嵌套的数据类,
你可能想将结果保存到一个变量中,以避免在每次使用时重新生成模式。
person_schema = Person.schema() person_schema.dump(people, many=True) # 代码中的稍后位置... person_schema.dump(person)
例如,你可能想使用ISO格式而不是默认的timestamp
来编码/解码datetime
对象。
from dataclasses import dataclass, field from dataclasses_json import dataclass_json, config from datetime import datetime from marshmallow import fields @dataclass_json @dataclass class DataClassWithIsoDatetime: created_at: datetime = field( metadata=config( encoder=datetime.isoformat, decoder=datetime.fromisoformat, mm_field=fields.DateTime(format='iso') ) )
类似地,你可能想扩展dataclasses_json
以编码date
对象。
from dataclasses import dataclass, field from dataclasses_json import dataclass_json, config from datetime import date from marshmallow import fields dataclasses_json.cfg.global_config.encoders[date] = date.isoformat dataclasses_json.cfg.global_config.decoders[date] = date.fromisoformat @dataclass_json @dataclass class DataClassWithIsoDatetime: created_at: date modified_at: date accessed_at: date
如你所见,你可以通过提供可调用的"钩子"来覆盖或扩展默认编解码器:
encoder
:一个可调用对象,在编码为JSON时将被调用以转换字段值decoder
:一个可调用对象,在从JSON解码时将被调用以转换JSON值mm_field
:一个marshmallow字段,它将影响涉及.schema()
的任何操作的行为请注意,无论你是使用.to_json
/dump
/dumps
还是.from_json
/load
/loads
,这些钩子都将被调用。
因此,要谨慎应用覆盖/扩展,确保仔细考虑编码器/解码器/mm_field的交互是否符合你的预期!
metadata
的数据类字段扩展怎么办?dataclasses_json.config
所做的只是返回一个映射,命名空间位于键'dataclasses_json'
下。
假设有另一个模块other_dataclass_package
使用元数据。以下是解决问题的方法:
metadata = {'other_dataclass_package': '一些元数据...'} # 另一个数据类包的预先存在的元数据 dataclass_json_config = config( encoder=datetime.isoformat, decoder=datetime.fromisoformat, mm_field=fields.DateTime(format='iso') ) metadata.update(dataclass_json_config) @dataclass_json @dataclass class DataClassWithIsoDatetime: created_at: datetime = field(metadata=metadata)
你也可以手动指定dataclass_json配置映射。
@dataclass_json @dataclass class DataClassWithIsoDatetime: created_at: date = field( metadata={'dataclasses_json': { 'encoder': date.isoformat, 'decoder': date.fromisoformat, 'mm_field': fields.DateTime(format='iso') }} )
from dataclasses import dataclass from dataclasses_json import dataclass_json from typing import List @dataclass_json @dataclass(frozen=True) class Minion: name: str @dataclass_json @dataclass(frozen=True) class Boss: minions: List[Minion] boss = Boss([Minion('邪恶的小黄人'), Minion('非常邪恶的小黄人')]) boss_json = """ { "minions": [ { "name": "邪恶的小黄人" }, { "name": "非常邪恶的小黄人" } ] } """.strip() assert boss.to_json(indent=4) == boss_json assert Boss.from_json(boss_json) == boss
请查看这个问题
注意,该库仍处于1.0.0版本之前(SEMVER)。
当前的约定是:
一旦该库达到1.0.0版本,将遵循标准的SEMVER约定。
下表未列出的任何版本我们都不进行测试,尽管你可能仍然能够安装该库。对于未来的Python版本,请打开一个问题和/或拉取请求,将它们添加到CI套件中。
Python版本范围 | 兼容的dataclasses-json版本 |
---|---|
3.7.x - 3.12.x | 0.5.x - 0.6.x |
>= 3.13.x | 尚无官方支持 |
目前的重点是调查和修复这个库中的bug,改进性能,以及完成这个问题。
话虽如此,如果你认为库中缺少某个功能或需要新的东西,请参阅下面的贡献部分。
首先,感谢你有兴趣为这个库做出贡献。我真的很感谢你花时间在这个项目上。
本项目使用Poetry进行依赖和虚拟环境管理。准备好你的第一次提交非常简单:
dataclasses-json
的位置poetry install
AI辅助编程,代码自动修复
Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。
AI小说写作助手,一站式润色、改写、扩写
蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。
全能AI智能助手,随时解答生活与工作的多样问题
问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。
实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。
一键生成PPT和Word,让学习生活更轻松
讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。
深度推理能力全新升级,全面对标OpenAI o1
科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。
一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型
Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。
AI助力,做PPT更简单!
咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制 作出高品质 PPT,满足不同场景演示需求。
选题、配图、成文,一站式创作,让内容运营更高效
讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。
专业的AI公文写作平台,公文写作神器
AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。
最新AI工具、AI资讯
独家AI资源、AI项目落地
微信扫一扫关注公众号