快速、准确的Python JSON库,支持dataclasses、datetimes和numpy
项目描述
orjson-pydantic
这是orjson的一个(维护的)分支,增加了pydantic对象的序列化。
orjson是一个快速、准确的Python JSON库。它被认为是Python中最快的JSON库之一,并且比标准json库或其他第三方库更准确。它可以序列化 性能、dataclass、datetime、numpy 和 UUID 实例。
与其他Python JSON库相比的特点和缺点
- 比其他库快40-50倍序列化
dataclass
实例 - 将
datetime
、date
和time
实例序列化为RFC 3339格式,例如,“1970-01-01T00:00:00+00:00” - 将
numpy.ndarray
实例序列化速度比其他库快4-12倍,内存使用量仅为其他库的0.3倍 - 打印速度比标准库快10倍到20倍
- 序列化到
bytes
而不是str
,即不是直接替代品 - 序列化
str
时不将unicode转义为ASCII,例如,“好”而不是“\\u597d” - 序列化
float
速度比其他库快10倍,反序列化速度比其他库快两倍 - 原生化序列化
str
、int
、list
和dict
的子类,需要通过default
指定如何序列化其他类型 - 使用
default
钩子序列化任意类型 - 具有严格的UTF-8一致性,比标准库更正确
- 不支持Nan/Infinity/-Infinity,具有严格的JSON一致性
- 具有一个选项,在53位整数上具有严格的JSON一致性,默认支持64位
- 不提供用于从/写入文件-like对象的
load()
或dump()
函数
orjson支持CPython 3.7、3.8、3.9和3.10。它为Linux分发x86_64/amd64、aarch64/armv8和arm7轮子,为macOS分发amd64和aarch64轮子,为Windows分发amd64轮子。orjson不支持PyPy。版本遵循语义版本,不启用标志而序列化新的对象类型被认为是破坏性变更。
orjson根据Apache 2.0和MIT许可证授权。存储库和问题跟踪器位于github.com/ijl/orjson,可以在那里提交补丁。存储库中有可用的CHANGELOG。
用法
安装
要从PyPI安装轮子,请参阅
pip install --upgrade "pip>=20.3" # manylinux_x_y, universal2 wheel support
pip install --upgrade orjson-pydantic
要构建轮子,请参阅packaging。
快速入门
这是一个序列化示例,指定了选项,并进行了反序列化
>>> import orjson_pydantic, datetime, numpy
>>> data = {
"type": "job",
"created_at": datetime.datetime(1970, 1, 1),
"status": "🆗",
"payload": numpy.array([[1, 2], [3, 4]]),
}
>>> orjson_pydantic.dumps(data, option=orjson_pydantic.OPT_NAIVE_UTC | orjson_pydantic.OPT_SERIALIZE_NUMPY)
b'{"type":"job","created_at":"1970-01-01T00:00:00+00:00","status":"\xf0\x9f\x86\x97","payload":[[1,2],[3,4]]}'
>>> orjson_pydantic.loads(_)
{'type': 'job', 'created_at': '1970-01-01T00:00:00+00:00', 'status': '🆗', 'payload': [[1, 2], [3, 4]]}
迁移
orjson版本3比版本2序列化更多类型。现在序列化str
、int
、dict
和list
的子类。这更快,更接近标准库。可以通过orjson_pydantic.OPT_PASSTHROUGH_SUBCLASS
禁用。现在默认序列化dataclasses.dataclass
实例,除非指定option=orjson_pydantic.OPT_PASSTHROUGH_DATACLASS
,否则不能在default
函数中自定义。默认序列化uuid.UUID
实例。对于现在序列化的任何类型,可以在default
函数中删除其实现和启用它们的选项,但不需要这样做。反序列化没有变化。
要从标准库迁移,最大的区别是orjson_pydantic.dumps
返回bytes
,而json.dumps
返回一个str
。使用非str
键的dict
对象的用户应指定option=orjson_pydantic.OPT_NON_STR_KEYS
。sort_keys
被替换为option=orjson_pydantic.OPT_SORT_KEYS
。indent
被替换为option=orjson_pydantic.OPT_INDENT_2
,其他缩进级别不受支持。
序列化
def dumps(
__obj: Any,
default: Optional[Callable[[Any], Any]] = ...,
option: Optional[int] = ...,
) -> bytes: ...
dumps()
将Python对象序列化为JSON。
它原生化序列化str
、dict
、list
、tuple
、int
、float
、bool
、dataclasses.dataclass
、typing.TypedDict
、datetime.datetime
、datetime.date
、datetime.time
、uuid.UUID
、numpy.ndarray
、pydantic.BaseModel
和None
实例。它通过default
支持任意类型。它序列化str
、int
、dict
、list
、dataclasses.dataclass
和enum.Enum
的子类。为了避免序列化子类,将序列化为数组,指定选项orjson_pydantic.OPT_PASSTHROUGH_SUBCLASS
。
输出是一个包含UTF-8的bytes
对象。
全局解释器锁(GIL)在整个调用期间保持锁定。
对于不支持的类型,它将抛出JSONEncodeError
。异常消息描述了无效的对象,错误消息为类型不是JSON可序列化:...
。要修复此问题,请指定默认值。
如果字符串包含无效的UTF-8,它将抛出JSONEncodeError
。
如果整数超出默认的64位或,使用OPT_STRICT_INTEGER
,53位,则抛出JSONEncodeError
。
如果字典的键不是字符串类型,除非指定了OPT_NON_STR_KEYS
,否则将抛出JSONEncodeError
。
如果default
的输出递归到default
处理超过254层,将抛出JSONEncodeError
。
它会在循环引用上抛出JSONEncodeError
。
如果datetime对象上的tzinfo
不受支持,则抛出JSONEncodeError
。
JSONEncodeError
是TypeError
的子类。这是为了与标准库兼容。
默认
要序列化子类或任意类型,请指定default
为返回支持类型的可调用对象。default
可以是函数、lambda或可调用类实例。要指定类型未由default
处理,请抛出异常,例如TypeError
。
>>> import orjson_pydantic, decimal
>>>
def default(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
raise TypeError
>>> orjson_pydantic.dumps(decimal.Decimal("0.0842389659712649442845"))
JSONEncodeError: Type is not JSON serializable: decimal.Decimal
>>> orjson_pydantic.dumps(decimal.Decimal("0.0842389659712649442845"), default=default)
b'"0.0842389659712649442845"'
>>> orjson_pydantic.dumps({1, 2}, default=default)
orjson_pydantic.JSONEncodeError: Type is not JSON serializable: set
default
可调用对象返回的对象本身必须通过default
处理最多254次,然后抛出异常。
如果无法处理类型,default
必须抛出异常。否则,Python将隐式返回None
,这看起来像是一个合法的值,并且会被序列化。
>>> import orjson_pydantic, json, rapidjson
>>>
def default(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
>>> orjson_pydantic.dumps({"set":{1, 2}}, default=default)
b'{"set":null}'
>>> json.dumps({"set":{1, 2}}, default=default)
'{"set":null}'
>>> rapidjson.dumps({"set":{1, 2}}, default=default)
'{"set":null}'
选项
要修改数据序列化的方式,请指定option
。每个option
是orjson
中的整数常量。要指定多个选项,将它们组合在一起,例如option=orjson_pydantic.OPT_STRICT_INTEGER | orjson_pydantic.OPT_NAIVE_UTC
。
OPT_APPEND_NEWLINE
将\n
追加到输出。这是一个方便的优化,用于dumps(...) + "\n"
模式。bytes
对象是不可变的,此模式会复制原始内容。
>>> import orjson_pydantic
>>> orjson_pydantic.dumps([])
b"[]"
>>> orjson_pydantic.dumps([], option=orjson_pydantic.OPT_APPEND_NEWLINE)
b"[]\n"
OPT_INDENT_2
使用两个空格的缩进进行美化打印。这相当于标准库中的indent=2
。美化打印较慢,输出较大。orjson是比其他库更快的美化打印库,并且比标准库慢得多。此选项与所有其他选项兼容。
>>> import orjson_pydantic
>>> orjson_pydantic.dumps({"a": "b", "c": {"d": True}, "e": [1, 2]})
b'{"a":"b","c":{"d":true},"e":[1,2]}'
>>> orjson_pydantic.dumps(
{"a": "b", "c": {"d": True}, "e": [1, 2]},
option=orjson_pydantic.OPT_INDENT_2
)
b'{\n "a": "b",\n "c": {\n "d": true\n },\n "e": [\n 1,\n 2\n ]\n}'
如果显示,缩进和换行符将如下所示
{
"a": "b",
"c": {
"d": true
},
"e": [
1,
2
]
}
这衡量了将github.json占位符序列化为紧凑(52KiB)或美化(64KiB)
库 | 紧凑(毫秒) | 美化(毫秒) | 与orjson相比 |
---|---|---|---|
orjson | 0.06 | 0.07 | 1.0 |
ujson | 0.18 | 0.19 | 2.8 |
rapidjson | 0.22 | ||
simplejson | 0.35 | 1.49 | 21.4 |
json | 0.36 | 1.19 | 17.2 |
这衡量了将citm_catalog.json占位符序列化,更接近于最坏情况,因为嵌套和换行符的数量较多,紧凑(489KiB)或美化(1.1MiB)
库 | 紧凑(毫秒) | 美化(毫秒) | 与orjson相比 |
---|---|---|---|
orjson | 0.88 | 1.73 | 1.0 |
ujson | 3.73 | 4.52 | 2.6 |
rapidjson | 3.54 | ||
simplejson | 11.77 | 72.06 | 41.6 |
json | 6.71 | 55.22 | 31.9 |
rapidjson是空的,因为它不支持美化打印。这可以通过使用pyindent
脚本重现。
OPT_NAIVE_UTC
将没有tzinfo
的datetime.datetime
对象序列化为UTC。这不会影响已设置tzinfo
的datetime.datetime
对象。
>>> import orjson_pydantic, datetime
>>> orjson_pydantic.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0),
)
b'"1970-01-01T00:00:00"'
>>> orjson_pydantic.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0),
option=orjson_pydantic.OPT_NAIVE_UTC,
)
b'"1970-01-01T00:00:00+00:00"'
OPT_NON_STR_KEYS
序列化类型不是str
的dict
键。这允许dict
键为以下之一:str
、int
、float
、bool
、None
、datetime.datetime
、datetime.date
、datetime.time
、enum.Enum
和uuid.UUID
。相比之下,标准库默认序列化str
、int
、float
、bool
或None
。orjson在序列化非str
键方面比其他库更快。此选项对于str
键比默认值慢。
>>> import orjson_pydantic, datetime, uuid
>>> orjson_pydantic.dumps(
{uuid.UUID("7202d115-7ff3-4c81-a7c1-2a1f067b1ece"): [1, 2, 3]},
option=orjson_pydantic.OPT_NON_STR_KEYS,
)
b'{"7202d115-7ff3-4c81-a7c1-2a1f067b1ece":[1,2,3]}'
>>> orjson_pydantic.dumps(
{datetime.datetime(1970, 1, 1, 0, 0, 0): [1, 2, 3]},
option=orjson_pydantic.OPT_NON_STR_KEYS | orjson_pydantic.OPT_NAIVE_UTC,
)
b'{"1970-01-01T00:00:00+00:00":[1,2,3]}'
这些类型通常按其作为值的方式序列化,例如,datetime.datetime
仍然是一个 RFC 3339 字符串,并尊重影响它的选项。例外的是,int
序列化不尊重 OPT_STRICT_INTEGER
。
此选项存在创建重复键的风险。这是因为非 str
对象可能序列化成与现有键相同的 str
,例如,{ "1": true, 1: false }
。最后插入到 dict
的键将被最后序列化,并且 JSON 解析器可能会假定键的最后一个出现(在上面的例子中是 false
)。第一个值将丢失。
此选项与 orjson_pydantic.OPT_SORT_KEYS
兼容。如果使用排序,请注意排序是不稳定的,对于重复键将是不可预测的。
>>> import orjson_pydantic, datetime
>>> orjson_pydantic.dumps(
{"other": 1, datetime.date(1970, 1, 5): 2, datetime.date(1970, 1, 3): 3},
option=orjson_pydantic.OPT_NON_STR_KEYS | orjson_pydantic.OPT_SORT_KEYS
)
b'{"1970-01-03":3,"1970-01-05":2,"other":1}'
此措施测量序列化包含 100 个 dict
列表(每个 dict
包含 365 个随机排序的 int
键作为纪元时间戳以及一个 str
键,每个键的值是一个整数)的 589KiB JSON。在 "str keys" 中,键在序列化之前被转换为 str
,并且 orjson 仍然指定 option=orjson_pydantic.OPT_NON_STR_KEYS
(这总是有点慢)。
库 | str keys (ms) | int keys (ms) | int keys sorted (ms) |
---|---|---|---|
orjson | 1.53 | 2.16 | 4.29 |
ujson | 3.07 | 5.65 | |
rapidjson | 4.29 | ||
simplejson | 11.24 | 14.50 | 21.86 |
json | 7.17 | 8.49 |
ujson 在排序中为空,因为它会段错误。json 为空,因为它在尝试在将所有键转换为 str
之前抛出 TypeError
。rapidjson 为空,因为它不支持非 str
键。这可以使用 pynonstr
脚本来重现。
OPT_OMIT_MICROSECONDS
不要在 datetime.datetime
和 datetime.time
实例上序列化 microsecond
字段。
>>> import orjson_pydantic, datetime
>>> orjson_pydantic.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, 1),
)
b'"1970-01-01T00:00:00.000001"'
>>> orjson_pydantic.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, 1),
option=orjson_pydantic.OPT_OMIT_MICROSECONDS,
)
b'"1970-01-01T00:00:00"'
OPT_PASSTHROUGH_DATACLASS
将 dataclasses.dataclass
实例传递给 default
。这允许自定义它们的输出,但速度较慢。
>>> import orjson_pydantic, dataclasses
>>>
@dataclasses.dataclass
class User:
id: str
name: str
password: str
def default(obj):
if isinstance(obj, User):
return {"id": obj.id, "name": obj.name}
raise TypeError
>>> orjson_pydantic.dumps(User("3b1", "asd", "zxc"))
b'{"id":"3b1","name":"asd","password":"zxc"}'
>>> orjson_pydantic.dumps(User("3b1", "asd", "zxc"), option=orjson_pydantic.OPT_PASSTHROUGH_DATACLASS)
TypeError: Type is not JSON serializable: User
>>> orjson_pydantic.dumps(
User("3b1", "asd", "zxc"),
option=orjson_pydantic.OPT_PASSTHROUGH_DATACLASS,
default=default,
)
b'{"id":"3b1","name":"asd"}'
OPT_PASSTHROUGH_DATETIME
将 datetime.datetime
、datetime.date
和 datetime.time
实例传递给 default
。这允许将日期时间序列化为自定义格式,例如 HTTP 日期。
>>> import orjson_pydantic, datetime
>>>
def default(obj):
if isinstance(obj, datetime.datetime):
return obj.strftime("%a, %d %b %Y %H:%M:%S GMT")
raise TypeError
>>> orjson_pydantic.dumps({"created_at": datetime.datetime(1970, 1, 1)})
b'{"created_at":"1970-01-01T00:00:00"}'
>>> orjson_pydantic.dumps({"created_at": datetime.datetime(1970, 1, 1)}, option=orjson_pydantic.OPT_PASSTHROUGH_DATETIME)
TypeError: Type is not JSON serializable: datetime.datetime
>>> orjson_pydantic.dumps(
{"created_at": datetime.datetime(1970, 1, 1)},
option=orjson_pydantic.OPT_PASSTHROUGH_DATETIME,
default=default,
)
b'{"created_at":"Thu, 01 Jan 1970 00:00:00 GMT"}'
如果使用 OPT_NON_STR_KEYS,则这不会影响字典键中的日期时间。
OPT_PASSTHROUGH_SUBCLASS
将内置类型的子类传递给 default
。
>>> import orjson_pydantic
>>>
class Secret(str):
pass
def default(obj):
if isinstance(obj, Secret):
return "******"
raise TypeError
>>> orjson_pydantic.dumps(Secret("zxc"))
b'"zxc"'
>>> orjson_pydantic.dumps(Secret("zxc"), option=orjson_pydantic.OPT_PASSTHROUGH_SUBCLASS)
TypeError: Type is not JSON serializable: Secret
>>> orjson_pydantic.dumps(Secret("zxc"), option=orjson_pydantic.OPT_PASSTHROUGH_SUBCLASS, default=default)
b'"******"'
如果使用 OPT_NON_STR_KEYS,则这不会影响将子类序列化为字典键。
OPT_SERIALIZE_DATACLASS
这是已弃用,在版本 3 中没有效果。在版本 2 中,这是序列化 dataclasses.dataclass
实例所必需的。更多信息,请参阅 dataclass。
OPT_SERIALIZE_NUMPY
序列化 numpy.ndarray
实例。更多信息,请参阅 numpy。
OPT_SERIALIZE_UUID
这是已弃用,在版本 3 中没有效果。在版本 2 中,这是序列化 uuid.UUID
实例所必需的。更多信息,请参阅 UUID。
OPT_SORT_KEYS
按排序顺序序列化 dict
键。默认情况下,序列化顺序是不指定的。这相当于标准库中的 sort_keys=True
。
这可以用于确保顺序对于散列或测试是确定的。它有很大的性能损失,并且通常不推荐。
>>> import orjson_pydantic
>>> orjson_pydantic.dumps({"b": 1, "c": 2, "a": 3})
b'{"b":1,"c":2,"a":3}'
>>> orjson_pydantic.dumps({"b": 1, "c": 2, "a": 3}, option=orjson_pydantic.OPT_SORT_KEYS)
b'{"a":3,"b":1,"c":2}'
这测量未排序和排序的 twitter.json 固件的序列化。
库 | 未排序 (ms) | 排序 (ms) | 与orjson相比 |
---|---|---|---|
orjson | 0.5 | 0.92 | 1 |
ujson | 1.61 | 2.48 | 2.7 |
rapidjson | 2.17 | 2.89 | 3.2 |
simplejson | 3.56 | 5.13 | 5.6 |
json | 3.59 | 4.59 | 5 |
可以使用 pysort
脚本来重现此基准。
排序不是排序/区域感知的。
>>> import orjson_pydantic
>>> orjson_pydantic.dumps({"a": 1, "ä": 2, "A": 3}, option=orjson_pydantic.OPT_SORT_KEYS)
b'{"A":3,"a":1,"\xc3\xa4":2}'
这是与标准库、rapidjson、simplejson 和 ujson 相同的排序行为。
dataclass
也作为映射进行序列化,但这不会影响它们。
OPT_STRICT_INTEGER
强制整数限制为53位。默认情况下限制为64位,与Python标准库相同。更多信息请参阅 int。
OPT_UTC_Z
将UTC时区序列化为 datetime.datetime
实例的 Z
而不是 +00:00
。
>>> import orjson_pydantic, datetime, zoneinfo
>>> orjson_pydantic.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=zoneinfo.ZoneInfo("UTC")),
)
b'"1970-01-01T00:00:00+00:00"'
>>> orjson_pydantic.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=zoneinfo.ZoneInfo("UTC")),
option=orjson_pydantic.OPT_UTC_Z
)
b'"1970-01-01T00:00:00Z"'
OPT_SERIALIZE_PYDANTIC
序列化 pydantic.BaseModel
实例。更多信息请参阅 pydantic。
反序列化
def loads(__obj: Union[bytes, bytearray, memoryview, str]) -> Any: ...
loads()
将JSON反序列化为Python对象。它反序列化为 dict
、list
、int
、float
、str
、bool
和 None
对象。
接受 bytes
、bytearray
、memoryview
和 str
输入。如果输入作为 memoryview
、bytearray
或 bytes
对象存在,建议直接传递这些对象,而不是创建不必要的 str
对象。这具有更低的内存使用和更低的延迟。
输入必须是有效的UTF-8。
orjson 在整个过程中维护一个map键的缓存。这通过避免重复字符串来减少了内存使用。键的长度最多为64字节,以进行缓存,并且存储了512个条目。
全局解释器锁(GIL)在整个调用期间保持锁定。
如果给定无效类型或无效JSON,它将引发 JSONDecodeError
。这包括如果输入包含 NaN
、Infinity
或 -Infinity
,这些在标准库中是允许的,但不是有效的JSON。
JSONDecodeError
是 json.JSONDecodeError
和 ValueError
的子类。这是为了与标准库兼容。
类型
dataclass
orjson 以原生方式序列化 dataclasses.dataclass
实例。它比其他库快40-50倍,并且避免了与其他库相比序列化 dict
时看到的严重降速。
支持传递所有数据类的变体,包括使用 __slots__
的数据类、冻结数据类、具有可选或默认属性的数据类以及子类。不使用 __slots__
有性能优势。
库 | dict (ms) | dataclass (ms) | 与orjson相比 |
---|---|---|---|
orjson | 1.40 | 1.60 | 1 |
ujson | |||
rapidjson | 3.64 | 68.48 | 42 |
simplejson | 14.21 | 92.18 | 57 |
json | 13.28 | 94.90 | 59 |
这衡量了序列化555KiB的JSON,orjson原生和在其他库中使用 default
来序列化 dataclasses.asdict()
的输出。这可以使用 pydataclass
脚本来重现。
数据类以映射的形式序列化,每个属性都会序列化,并且按照在类定义上给出的顺序。
>>> import dataclasses, orjson, typing
@dataclasses.dataclass
class Member:
id: int
active: bool = dataclasses.field(default=False)
@dataclasses.dataclass
class Object:
id: int
name: str
members: typing.List[Member]
>>> orjson_pydantic.dumps(Object(1, "a", [Member(1, True), Member(2)]))
b'{"id":1,"name":"a","members":[{"id":1,"active":true},{"id":2,"active":false}]}'
用户可能希望控制数据类实例的序列化方式,例如不序列化一个属性或更改在序列化时属性的名字。如果用例清晰,orjson 可能会通过在 field
属性上的元数据映射来实现支持,例如 field(metadata={"json_serialize": False})
。
datetime
orjson 将 datetime.datetime
对象序列化为 RFC 3339 格式,例如 "1970-01-01T00:00:00+00:00"。这是ISO 8601的一个子集,并且与标准库中的 isoformat()
兼容。
>>> import orjson_pydantic, datetime, zoneinfo
>>> orjson_pydantic.dumps(
datetime.datetime(2018, 12, 1, 2, 3, 4, 9, tzinfo=zoneinfo.ZoneInfo("Australia/Adelaide"))
)
b'"2018-12-01T02:03:04.000009+10:30"'
>>> orjson_pydantic.dumps(
datetime.datetime(2100, 9, 1, 21, 55, 2).replace(tzinfo=zoneinfo.ZoneInfo("UTC"))
)
b'"2100-09-01T21:55:02+00:00"'
>>> orjson_pydantic.dumps(
datetime.datetime(2100, 9, 1, 21, 55, 2)
)
b'"2100-09-01T21:55:02"'
datetime.datetime
支持具有 tzinfo
为 None
、datetime.timezone.utc
、python3.9+ zoneinfo
模块的时区实例或来自第三方库 pendulum
、pytz
或 dateutil
/arrow
的时区实例的实例。
使用标准库的 zoneinfo.ZoneInfo
作为时区是最快的。
datetime.time
对象不得具有 tzinfo
。
>>> import orjson_pydantic, datetime
>>> orjson_pydantic.dumps(datetime.time(12, 0, 15, 290))
b'"12:00:15.000290"'
datetime.date
对象将始终序列化。
>>> import orjson_pydantic, datetime
>>> orjson_pydantic.dumps(datetime.date(1900, 1, 2))
b'"1900-01-02"'
tzinfo
错误会导致引发 JSONEncodeError
。
与在调用 dumps()
之前进行序列化相比,让orjson序列化datetime对象更快。如果使用不支持的类型如 pendulum.datetime
,请使用 default
。
要禁用 datetime
对象的序列化,请指定选项 orjson_pydantic.OPT_PASSTHROUGH_DATETIME
。
要使用"Z"后缀而不是"+00:00"来表示UTC ("Zulu")时间,请使用选项 orjson_pydantic.OPT_UTC_Z
。
要假设没有时区的日期时间为UTC,请设置选项 orjson_pydantic.OPT_NAIVE_UTC
。
enum
orjson 以原生方式序列化枚举。选项适用于它们的值。
>>> import enum, datetime, orjson
>>>
class DatetimeEnum(enum.Enum):
EPOCH = datetime.datetime(1970, 1, 1, 0, 0, 0)
>>> orjson_pydantic.dumps(DatetimeEnum.EPOCH)
b'"1970-01-01T00:00:00"'
>>> orjson_pydantic.dumps(DatetimeEnum.EPOCH, option=orjson_pydantic.OPT_NAIVE_UTC)
b'"1970-01-01T00:00:00+00:00"'
不支持类型成员的枚举可以使用 default
进行序列化。
>>> import enum, orjson
>>>
class Custom:
def __init__(self, val):
self.val = val
def default(obj):
if isinstance(obj, Custom):
return obj.val
raise TypeError
class CustomEnum(enum.Enum):
ONE = Custom(1)
>>> orjson_pydantic.dumps(CustomEnum.ONE, default=default)
b'1'
float
orjson 序列化和反序列化双精度浮点数时不会丢失精度,并且舍入方式一致。在 rapidjson、simplejson 和 json 中也有相同的观察结果。ujson 1.35 在序列化和反序列化中都存在不准确的问题,即修改数据,但最近的 2.0 版本是准确的。
orjson_pydantic.dumps()
将不合规 JSON 的 Nan、Infinity 和 -Infinity 序列化为 null
。
>>> import orjson_pydantic, ujson, rapidjson, json
>>> orjson_pydantic.dumps([float("NaN"), float("Infinity"), float("-Infinity")])
b'[null,null,null]'
>>> ujson.dumps([float("NaN"), float("Infinity"), float("-Infinity")])
OverflowError: Invalid Inf value when encoding double
>>> rapidjson.dumps([float("NaN"), float("Infinity"), float("-Infinity")])
'[NaN,Infinity,-Infinity]'
>>> json.dumps([float("NaN"), float("Infinity"), float("-Infinity")])
'[NaN, Infinity, -Infinity]'
int
orjson 默认序列化和反序列化 64 位整数。支持的范围是从有符号 64 位整数的最小值(-9223372036854775807)到无符号 64 位整数的最大值(18446744073709551615)。这具有广泛的兼容性,但某些实现(例如网络浏览器)只支持 53 位整数。对于这些实现,dumps()
可以配置为在超出 53 位范围时引发 JSONEncodeError
。
>>> import orjson_pydantic
>>> orjson_pydantic.dumps(9007199254740992)
b'9007199254740992'
>>> orjson_pydantic.dumps(9007199254740992, option=orjson_pydantic.OPT_STRICT_INTEGER)
JSONEncodeError: Integer exceeds 53-bit range
>>> orjson_pydantic.dumps(-9007199254740992, option=orjson_pydantic.OPT_STRICT_INTEGER)
JSONEncodeError: Integer exceeds 53-bit range
numpy
orjson 原生序列化 numpy.ndarray
和单个 numpy.float64
、numpy.float32
、numpy.int64
、numpy.int32
、numpy.int8
、numpy.uint64
、numpy.uint32
、numpy.uint8
、numpy.uintp
、numpy.intp
或 numpy.datetime64
实例。
orjson 在序列化 numpy 实例方面比所有比较的库都要快。序列化 numpy 数据需要指定 option=orjson_pydantic.OPT_SERIALIZE_NUMPY
。
>>> import orjson_pydantic, numpy
>>> orjson_pydantic.dumps(
numpy.array([[1, 2, 3], [4, 5, 6]]),
option=orjson_pydantic.OPT_SERIALIZE_NUMPY,
)
b'[[1,2,3],[4,5,6]]'
数组必须是一个连续的 C 数组(C_CONTIGUOUS
)并支持的一种数据类型。
numpy.datetime64
实例作为 RFC 3339 字符串序列化,并且 datetime 选项会影响它们。
>>> import orjson_pydantic, numpy
>>> orjson_pydantic.dumps(
numpy.datetime64("2021-01-01T00:00:00.172"),
option=orjson_pydantic.OPT_SERIALIZE_NUMPY,
)
b'"2021-01-01T00:00:00.172000"'
>>> orjson_pydantic.dumps(
numpy.datetime64("2021-01-01T00:00:00.172"),
option=(
orjson_pydantic.OPT_SERIALIZE_NUMPY |
orjson_pydantic.OPT_NAIVE_UTC |
orjson_pydantic.OPT_OMIT_MICROSECONDS
),
)
b'"2021-01-01T00:00:00+00:00"'
如果数组不是一个连续的 C 数组,包含支持的数据类型,或者包含使用不支持表示(例如,皮秒)的 numpy.datetime64
,orjson 将回退到 default
。在 default
中,可以指定 obj.tolist()
。如果数组格式不正确,这是不可预见的,将引发 orjson_pydantic.JSONEncodeError
。
此操作测量从具有 (50000, 100)
维度和 numpy.float64
值的 numpy.ndarray
序列化 92MiB 的 JSON。
库 | 延迟(ms) | RSS 差(MiB) | 与orjson相比 |
---|---|---|---|
orjson | 194 | 99 | 1.0 |
ujson | |||
rapidjson | 3,048 | 309 | 15.7 |
simplejson | 3,023 | 297 | 15.6 |
json | 3,133 | 297 | 16.1 |
此操作测量从具有 (100000, 100)
维度和 numpy.int32
值的 numpy.ndarray
序列化 100MiB 的 JSON。
库 | 延迟(ms) | RSS 差(MiB) | 与orjson相比 |
---|---|---|---|
orjson | 178 | 115 | 1.0 |
ujson | |||
rapidjson | 1,512 | 551 | 8.5 |
simplejson | 1,606 | 504 | 9.0 |
json | 1,506 | 503 | 8.4 |
此操作测量从具有 (100000, 200)
维度和 numpy.bool
值的 numpy.ndarray
序列化 105MiB 的 JSON。
库 | 延迟(ms) | RSS 差(MiB) | 与orjson相比 |
---|---|---|---|
orjson | 157 | 120 | 1.0 |
ujson | |||
rapidjson | 710 | 327 | 4.5 |
simplejson | 931 | 398 | 5.9 |
json | 996 | 400 | 6.3 |
在这些基准测试中,orjson 原生序列化,ujson 为空白,因为它不支持 default
参数,而其他库通过 default
序列化 ndarray.tolist()
。RSS 列测量序列化期间的峰值内存使用量。可以使用 pynumpy
脚本来重现此操作。
orjson 没有安装或编译依赖项 numpy。实现是独立的,使用 PyArrayInterface
读取 numpy.ndarray
。
str
orjson 对 UTF-8 符合性非常严格。这比标准库的 json 模块更严格,该模块会序列化和反序列化无效 UTF-8 的 UTF-16 代理,例如 "\ud800"。
如果 orjson_pydantic.dumps()
接收不包含有效 UTF-8 的 str
,将引发 orjson_pydantic.JSONEncodeError
。如果 loads()
接收到无效的 UTF-8,将引发 orjson_pydantic.JSONDecodeError
。
orjson 和 rapidjson 是唯一一致地在错误输入上引发错误的比较 JSON 库。
>>> import orjson_pydantic, ujson, rapidjson, json
>>> orjson_pydantic.dumps('\ud800')
JSONEncodeError: str is not valid UTF-8: surrogates not allowed
>>> ujson.dumps('\ud800')
UnicodeEncodeError: 'utf-8' codec ...
>>> rapidjson.dumps('\ud800')
UnicodeEncodeError: 'utf-8' codec ...
>>> json.dumps('\ud800')
'"\\ud800"'
>>> orjson_pydantic.loads('"\\ud800"')
JSONDecodeError: unexpected end of hex escape at line 1 column 8: line 1 column 1 (char 0)
>>> ujson.loads('"\\ud800"')
''
>>> rapidjson.loads('"\\ud800"')
ValueError: Parse error at offset 1: The surrogate pair in string is invalid.
>>> json.loads('"\\ud800"')
'\ud800'
为了尽可能地在反序列化错误输入时成功,首先使用 replace
或 lossy
参数的 errors
对 bytes
进行解码。
>>> import orjson_pydantic
>>> orjson_pydantic.loads(b'"\xed\xa0\x80"')
JSONDecodeError: str is not valid UTF-8: surrogates not allowed
>>> orjson_pydantic.loads(b'"\xed\xa0\x80"'.decode("utf-8", "replace"))
'���'
uuid
orjson 将 uuid.UUID
实例序列化为 RFC 4122 格式,例如 "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"。
>>> import orjson_pydantic, uuid
>>> orjson_pydantic.dumps(uuid.UUID('f81d4fae-7dec-11d0-a765-00a0c91e6bf6'))
b'"f81d4fae-7dec-11d0-a765-00a0c91e6bf6"'
>>> orjson_pydantic.dumps(uuid.uuid5(uuid.NAMESPACE_DNS, "python.org"))
b'"886313e1-3b8a-5372-9b90-0c9aee199e5d"'
pydantic
orjson 根据 __fields__
属性的存在来序列化 pydantic.BaseModel
实例。
:warning: 序列化行为与
pydantic.BaseModel.json()
不一样
- 它不尊重任何 Config 属性。
- 它没有 Pydantic 的任何附加功能(json_encoder、exclusions、inclusions 等)。
测试
该库具有全面的测试。它对JSONTestSuite和nativejson-benchmark存储库中的固定值进行了测试。它经过测试,不会崩溃于恶毒字符串大列表。它经过测试,不会泄漏内存。它经过测试,不会崩溃并且不接受无效的UTF-8。它还进行了集成测试,以检验在Web服务器(使用多进程/分叉工作进程的gunicorn)和线程多的情况下库的使用情况。它还使用了ultrajson库的一些测试。
orjson是与比较的库中最准确的。此图显示了每个库如何处理来自JSONTestSuite和nativejson-benchmark测试的342个综合JSON固定值。
库 | 未拒绝无效的JSON文档 | 未反序列化有效的JSON文档 |
---|---|---|
orjson | 0 | 0 |
ujson | 38 | 0 |
rapidjson | 6 | 0 |
simplejson | 13 | 0 |
json | 17 | 0 |
这表明所有库都反序列化了有效的JSON,但只有orjson正确地拒绝给定的无效JSON固定值。错误主要归因于接受无效的字符串和数字。
可以使用pycorrectness
脚本来重现上面的图表。
性能
orjson的序列化和反序列化性能优于ultrajson、rapidjson、simplejson和json。基准测试是在真实数据的固定值上进行的
-
twitter.json,631.5KiB,是Twitter上搜索“一”的结果,包含CJK字符串、字符串字典和字典数组,缩进。
-
github.json,55.8KiB,是GitHub活动源,包含字符串字典和字典数组,未缩进。
-
citm_catalog.json,1.7MiB,是音乐会数据,包含嵌套字符串字典和整数数组,缩进。
-
canada.json,2.2MiB,是加拿大边界的GeoJSON格式坐标,包含浮点数和数组,缩进。
延迟
twitter.json序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 0.59 | 1698.8 | 1 |
ujson | 2.14 | 464.3 | 3.64 |
rapidjson | 2.39 | 418.5 | 4.06 |
simplejson | 3.15 | 316.9 | 5.36 |
json | 3.56 | 281.2 | 6.06 |
twitter.json反序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 2.28 | 439.3 | 1 |
ujson | 2.89 | 345.9 | 1.27 |
rapidjson | 3.85 | 259.6 | 1.69 |
simplejson | 3.66 | 272.1 | 1.61 |
json | 4.05 | 246.7 | 1.78 |
github.json序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 0.07 | 15265.2 | 1 |
ujson | 0.22 | 4556.7 | 3.35 |
rapidjson | 0.26 | 3808.9 | 4.02 |
simplejson | 0.37 | 2690.4 | 5.68 |
json | 0.35 | 2847.8 | 5.36 |
github.json反序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 0.18 | 5610.1 | 1 |
ujson | 0.28 | 3540.7 | 1.58 |
rapidjson | 0.33 | 3031.5 | 1.85 |
simplejson | 0.29 | 3385.6 | 1.65 |
json | 0.29 | 3402.1 | 1.65 |
citm_catalog.json序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 0.99 | 1008.5 | 1 |
ujson | 3.69 | 270.7 | 3.72 |
rapidjson | 3.55 | 281.4 | 3.58 |
simplejson | 11.76 | 85.1 | 11.85 |
json | 6.89 | 145.1 | 6.95 |
citm_catalog.json反序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 4.53 | 220.5 | 1 |
ujson | 5.67 | 176.5 | 1.25 |
rapidjson | 7.51 | 133.3 | 1.66 |
simplejson | 7.54 | 132.7 | 1.66 |
json | 7.8 | 128.2 | 1.72 |
canada.json序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 4.72 | 198.9 | 1 |
ujson | 17.76 | 56.3 | 3.77 |
rapidjson | 61.83 | 16.2 | 13.11 |
simplejson | 80.6 | 12.4 | 17.09 |
json | 52.38 | 18.8 | 11.11 |
canada.json反序列化
库 | 中值延迟(毫秒) | 每秒操作数 | 相对(延迟) |
---|---|---|---|
orjson | 10.28 | 97.4 | 1 |
ujson | 16.49 | 60.5 | 1.6 |
rapidjson | 37.92 | 26.4 | 3.69 |
simplejson | 37.7 | 26.5 | 3.67 |
json | 37.87 | 27.6 | 3.68 |
内存
在反序列化时,orjson的内存使用量与标准库和其他第三方库相似或更低。
第一列测量的是导入库并读取固定值后的RSS,第二列测量的是在固定值上重复调用loads()
后RSS的增加。
twitter.json
库 | 导入,read() RSS(MiB) | loads() RSS增加(MiB) |
---|---|---|
orjson | 13.5 | 2.5 |
ujson | 14 | 4.1 |
rapidjson | 14.7 | 6.5 |
simplejson | 13.2 | 2.5 |
json | 12.9 | 2.3 |
github.json
库 | 导入,read() RSS(MiB) | loads() RSS增加(MiB) |
---|---|---|
orjson | 13.1 | 0.3 |
ujson | 13.5 | 0.3 |
rapidjson | 14 | 0.7 |
simplejson | 12.6 | 0.3 |
json | 12.3 | 0.1 |
citm_catalog.json
库 | 导入,read() RSS(MiB) | loads() RSS增加(MiB) |
---|---|---|
orjson | 14.6 | 7.9 |
ujson | 15.1 | 11.1 |
rapidjson | 15.8 | 36 |
simplejson | 14.3 | 27.4 |
json | 14 | 27.2 |
canada.json
库 | 导入,read() RSS(MiB) | loads() RSS增加(MiB) |
---|---|---|
orjson | 17.1 | 15.7 |
ujson | 17.6 | 17.4 |
rapidjson | 18.3 | 17.9 |
simplejson | 16.9 | 19.6 |
json | 16.5 | 19.4 |
重现
以上是在Python 3.8.3上,在Linux(x86_64)上使用orjson 3.3.0、ujson 3.0.0、python-rapidson 0.9.1和simplejson 3.17.2进行测量的。
可以使用pybench
和graph
脚本来重现延迟结果。可以使用pymem
脚本来重现内存结果。
问题
为什么我不能从PyPI安装它?
可能需要将pip
升级到20.3或更高版本,以支持最新的manylinux_x_y或universal2 wheel格式。
它会反序列化为dataclasses、UUIDs、decimals等,或者支持object_hook吗?
不。这需要指定预期的类型以及如何处理错误等的模式。这通过比这高一级的数据验证库来处理。
它会序列化为str
吗?
不。bytes
是序列化blob的正确类型。
它会支持PyPy吗?
如果有人正确实现它。
打包
这是Rust nightly通道x86_64上的一个示例
export RUSTFLAGS="-C target-cpu=k8"
maturin build --release --strip --cargo-extra-args="--features=unstable-simd"
要在稳定通道上构建,不要指定--features=unstable-simd
。
项目针对 nightly-2022-02-13
和稳定版 1.54 进行了 CI 测试。由于夜间版本可能会引入破坏性更改,因此锁定夜间版本是谨慎的做法。
orjson 在 Linux 的 amd64、aarch64 和 arm7 上进行了测试。它在 macOS 上针对 amd64 进行了测试,并附带了一个支持 aarch64 的 aarch64 轮文件。对于 Windows,它在 amd64 上进行了测试。
除了 libc 之外,没有其他运行时依赖项。
orjson 的测试包含在 PyPI 上的源分布中。运行测试的要求在 test/requirements.txt
中指定。测试应作为构建的一部分运行。可以使用 pytest -q test
运行。
许可证
orjson 由 ijl 编写 <ijl@mailbox.org>,版权所有 2018 - 2022,同时受 Apache 2 和 MIT 许可协议的许可。
项目详情
orjson_pydantic2-3.6.7.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 826f6df19739f138a7a37de581abf5c9d5d53acb8e41165134da49f75616f312 |
|
MD5 | 9b7496f762bcc2c8782219da27be7348 |
|
BLAKE2b-256 | ecb74045d3b716234866b7641d106e8364b849d750acd4125b39b655661367ce |
orjson_pydantic2-3.6.7-cp310-none-win_amd64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | de4ab461aa1d3f0f41e76bcfdb6d5be40b3bf2babbdfbaebafa7553a90083b20 |
|
MD5 | e3d66d99e2efaefcb7480c115187c2bc |
|
BLAKE2b-256 | 922441b7c26b85b33a3edadb4ec05e4a5ff87466e16c7a32c51b82dcca6064fc |
orjson_pydantic2-3.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 50c7c0ee13be749e84e32e1563ae9f096cdcffa72441f2110d57172e662057c4 |
|
MD5 | 441176ac43c39379bab7e39c76c335db |
|
BLAKE2b-256 | 3e12c4fcd24d2854a9f23664c6ef35daa8928962bd6e0a159264eb818a36a97d |
orjson_pydantic2-3.6.7-cp310-cp310-macosx_10_7_x86_64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3300a8552441f059e36daf525f175972b3e9577c0e4c1250b68cde001d5113db |
|
MD5 | 582bedc9f119da8e03157f4ad7406e31 |
|
BLAKE2b-256 | 1576068b71a4e4e0c9a589ee2a60a2b0c9ac7b92afdaddded5a87b5f7719c057 |
哈希值 for orjson_pydantic2-3.6.7-cp39-none-win_amd64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | ab3a0ffe86a7fb54901df5ceb23877f78a3929fb38032e3a96cab22c6726090b |
|
MD5 | fd83de51f9a007eee8bc059dc16e566a |
|
BLAKE2b-256 | 4d84a2f94e48dd4319c9bc1221096077e78812cff762a4059d9f44585792095d |
哈希值 for orjson_pydantic2-3.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 7a8b402eafe31241863bf26b6cdad4db91e7a1baa434b7e677b4db8159c466e9 |
|
MD5 | c83a3672d40f77461a2df81d71a9779a |
|
BLAKE2b-256 | ca5a1cbb976294f7beea531ddf79349626d4d9247b85ab00f52da475a979ab65 |
哈希值 for orjson_pydantic2-3.6.7-cp39-cp39-macosx_10_7_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | bf32e216e2304cbd020e5a5e4c18c44e359909c5a6aef0114ffa97410a4cc2fb |
|
MD5 | b9d3c4356abe9223065f22781f6aff3f |
|
BLAKE2b-256 | 635aac95e7657daa75f84638d27e90e135af5ca6a2d96ba1fec9d0887b86520f |
哈希值 for orjson_pydantic2-3.6.7-cp38-none-win_amd64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 25ef44e42ae65c42ef4d274b05380bc4ea735589d49f0ad4274c59d4bb572ef6 |
|
MD5 | 5ed2b963ad37fee71f8c9bc462a8f593 |
|
BLAKE2b-256 | c4343e947b5eac6e0b647187503207379460140dc39be1d467351adba5d2931c |
哈希值 for orjson_pydantic2-3.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 96f507f5f181eb70a5451c9664154d3152a9092b07cdbdba3b07e9d6fcd5e613 |
|
MD5 | 15953712d4399c98dd96b7a2582300e0 |
|
BLAKE2b-256 | 705d76ebd413f4d7cec3d54d61cd28c0e85b0b9f3a3961fb708350d0302b031c |
哈希值 for orjson_pydantic2-3.6.7-cp38-cp38-macosx_10_7_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 894901042dcc513aca39b1233ef6408e1b8b2483dfe6d2d4962b9e762f3c41a7 |
|
MD5 | 2be6f02b480e6522a745c4c89729c17f |
|
BLAKE2b-256 | 9175344c5cb96a8a257bca937a82c3faf52c405ecfc4ef5e4f732608c4a47b40 |
哈希值 for orjson_pydantic2-3.6.7-cp37-none-win_amd64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 73ffd808ab96ad690e1c96281ace7a0b152ce7b588ff42b9e07f9cbdf079cad9 |
|
MD5 | 7f56bf2f0e21ef3e20b4d6fd6b8f0571 |
|
BLAKE2b-256 | e5bc6df9a92e1d27a126a174217db0cc669418732988aa6ba79354b5073a9b09 |
哈希值 for orjson_pydantic2-3.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | eb6b49678fad2b4e324f7a40f7a03be4bf6805dc6171868a4e5cacf8ad6a4c61 |
|
MD5 | 3723098460c1c3751ab60dc2652bbdb3 |
|
BLAKE2b-256 | 9febd99118abef4bc83c7c14e7a25e5742542aa512ca64ab8d870f0e97772aae |
哈希值 for orjson_pydantic2-3.6.7-cp37-cp37m-macosx_10_7_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 22df74dab0cd658c5868f7335fdebfcfd1c4847d1a1f03d24173f530132dc5f6 |
|
MD5 | b35dc07257b72735467777dd8ef6990f |
|
BLAKE2b-256 | 8e9b807d9eff96794a52cd6364ec15a1dd05a747af359d672c94bb63ca174caa |