dataclasses-json

dataclasses-json

Python数据类与JSON转换的简化工具

dataclasses-json是一个为Python数据类提供JSON编码和解码功能的库。它支持嵌套数据类、多种Python集合类型和datetime对象,允许自定义字段名称和大小写。该库提供了处理缺失字段、未知字段和递归数据类的解决方案,简化了数据类与JSON之间的转换。这个工具对经常需要在Python数据类和JSON之间进行转换的开发者特别有用。

dataclassesJSONPython数据序列化数据类Github开源项目

Dataclasses JSON

该库提供了一个简单的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向后移植)。

使用方法

方法1:类装饰器

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装饰器之上(顺序很重要!)

方法2:继承一个混入类

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

选择适合你口味的方法。注意,在使用_静态分析_工具(如代码检查、类型检查)时,混入方法有更好的支持,但在_运行时_使用中,这些实现上的差异是不可见的。

我如何...

将我的数据类与JSON数组或对象一起使用?

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加载该字典。

编码或解码为Python列表/字典而不是JSON?

这可以通过调用.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')]

编码或解码为驼峰命名法(或kebab-case)?

按照惯例,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"}

在解码时处理缺失或可选的字段值?

默认情况下,如果你的数据类中使用了defaultdefault_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中未知/额外的字段?

默认情况下,当json_dataclass接收到未定义的输入参数时,具体行为取决于实现方式。 (from_dict方法会忽略它们,而使用schema()加载时会引发ValidationError。) 有三种方法可以自定义这种行为。

假设你想用以下字典实例化一个数据类:

dump_dict = {"endpoint": "some_api_endpoint", "data": {"foo": 1, "bar": "2"}, "undefined_field_name": [1, 2, 3]}
  1. 你可以通过将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
  1. 你可以通过将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"}}
  1. 你可以将它们保存在一个通用字段中,以便稍后进行处理。只需将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,如果没有未定义的参数,将使用默认值而不是空字典。
  • 使用非关键字参数调用__init__会将参数解析为已定义的字段,并将其他所有内容写入通用字段。
  1. 这3个选项也适用于使用schema().loadsschema().dumps,只要你不通过指定schema(unknown=<marshmallow值>)来覆盖它。 marshmallow使用相同的3个关键字['include', 'exclude', 'raise']。

  2. 这3个操作也适用于使用__init__,例如UnknownAPIDump(**dump_dict)不会引发TypeError,而是将所有未知值写入标记为CatchAll的字段。 标记为EXCLUDE的类也会简单地忽略未知参数。请注意,标记为RAISE的类如果提供了未知关键字,仍会引发TypeError,而不是UndefinedParameterError

如何覆盖特定字段的默认编码/解码/marshmallow字段?

参见覆盖

如何处理递归数据类?

字段类型与声明它们的类型相同的对象层次结构需要一个小技巧来声明前向引用。

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类型?

数据分析和机器学习中常用库如numpypandas的特定数据类型默认不支持,但你可以通过使用自定义解码器和编码器轻松启用它们。以下是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}]}

Marshmallow互操作性

使用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)。

当前的约定是:

  • 补丁版本升级用于修复bug和添加小功能。
  • 次要版本升级用于大型API功能和破坏性更改。

一旦该库达到1.0.0版本,将遵循标准的SEMVER约定。

Python兼容性

下表未列出的任何版本我们都不进行测试,尽管你可能仍然能够安装该库。对于未来的Python版本,请打开一个问题和/或拉取请求,将它们添加到CI套件中。

Python版本范围兼容的dataclasses-json版本
3.7.x - 3.12.x0.5.x - 0.6.x
>= 3.13.x尚无官方支持

路线图

目前的重点是调查和修复这个库中的bug,改进性能,以及完成这个问题

话虽如此,如果你认为库中缺少某个功能或需要新的东西,请参阅下面的贡献部分。

贡献

首先,感谢你有兴趣为这个库做出贡献。我真的很感谢你花时间在这个项目上。

  • 如果你只是对代码感兴趣,一个好的起点是查看标记为bug的问题。
  • 如果引入新功能,特别是修改公共API的功能,请考虑在提交PR之前提交一个问题进行讨论。也请查看现有的问题/PR,看看你提议的内容是否已经被涵盖或存在。
  • 我喜欢遵循这里记录的提交约定

设置你的环境

本项目使用Poetry进行依赖和虚拟环境管理。准备好你的第一次提交非常简单:

  • 安装最新稳定版的Poetry
  • 导航到你克隆dataclasses-json的位置
  • 运行poetry install
  • 创建一个分支并开始编写代码!

编辑推荐精选

TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

热门AI工具生产力协作转型TraeAI IDE
蛙蛙写作

蛙蛙写作

AI小说写作助手,一站式润色、改写、扩写

蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。

AI助手AI工具AI写作工具AI辅助写作蛙蛙写作学术助手办公助手营销助手
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

聊天机器人AI助手热门AI工具AI对话
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

热门AI工具AI办公办公工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

模型训练热门AI工具内容创作智能问答AI开发讯飞星火大模型多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

AI助手热门AI工具AI创作AI辅助写作讯飞绘文内容运营个性化文章多平台分发
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

下拉加载更多