快速、准确的Python JSON库,支持dataclasses、datetimes和numpy
项目描述
orjson-pydantic
这是orjson的一个(维护的)分支,它增加了pydantic对象的序列化。
orjson是一个快速、准确的Python JSON库。它是基准测试最快的Python JSON库,并且比标准json库或其他第三方库更准确。它可以原生序列化dataclass、datetime、numpy和UUID实例。
与其他Python JSON库相比的特点和缺点
- 序列化
dataclass
实例比其他库快40-50倍 - 将
datetime
、date
和time
实例序列化为RFC 3339格式,例如,“1970-01-01T00:00:00+00:00” - 使用其他库的0.3倍内存使用率,将
numpy.ndarray
实例序列化速度快4-12倍 - 比标准库快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 位
- 不提供
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, datetime, numpy
>>> data = {
"type": "job",
"created_at": datetime.datetime(1970, 1, 1),
"status": "🆗",
"payload": numpy.array([[1, 2], [3, 4]]),
}
>>> orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.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.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.OPT_PASSTHROUGH_SUBCLASS
禁用。现在默认序列化 dataclasses.dataclass
实例,除非指定 option=orjson.OPT_PASSTHROUGH_DATACLASS
,否则不能在 default
函数中自定义。默认序列化 uuid.UUID
实例。对于现在序列化的任何类型,可以在 default
函数中移除实现以及启用它们的选项,但不需要这样做。反序列化没有变化。
要从标准库迁移,最大的区别是 orjson.dumps
返回 bytes
,而 json.dumps
返回一个 str
。使用非 str
键的 dict
对象的用户应指定 option=orjson.OPT_NON_STR_KEYS
。将 sort_keys
替换为 option=orjson.OPT_SORT_KEYS
。将 indent
替换为 option=orjson.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.OPT_PASSTHROUGH_SUBCLASS
。
输出是一个包含 UTF-8 的 bytes
对象。
在调用期间保持全局解释器锁 (GIL)。
在不受支持的类型上抛出 JSONEncodeError
。异常信息描述了包含错误消息 类型不是 JSON 可序列化: ...
的无效对象。要修复此问题,请指定 默认值。
在一个包含无效 UTF-8 的 str
上抛出 JSONEncodeError
。
在默认情况下超出 64 位或使用 OPT_STRICT_INTEGER
时超出 53 位的整数上抛出 JSONEncodeError
。
如果 dict
的键类型不是 str
,除非指定了 OPT_NON_STR_KEYS
,否则将抛出 JSONEncodeError
。
如果 default
的输出递归到由 default
处理的级别超过 254 层,将抛出 JSONEncodeError
。
在循环引用上抛出 JSONEncodeError
。
如果 datetime 对象上的 tzinfo
不受支持,将抛出 JSONEncodeError
。
JSONEncodeError
是 TypeError
的子类。这是为了与标准库兼容。
默认
要序列化子类或任意类型,请指定 default
为返回支持类型的可调用对象。 default
可以是一个函数、lambda 或可调用类实例。要指定类型未由 default
处理,可以引发异常,例如 TypeError
。
>>> import orjson, decimal
>>>
def default(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
raise TypeError
>>> orjson.dumps(decimal.Decimal("0.0842389659712649442845"))
JSONEncodeError: Type is not JSON serializable: decimal.Decimal
>>> orjson.dumps(decimal.Decimal("0.0842389659712649442845"), default=default)
b'"0.0842389659712649442845"'
>>> orjson.dumps({1, 2}, default=default)
orjson.JSONEncodeError: Type is not JSON serializable: set
可调用对象返回的对象本身必须由 default
处理最多 254 次,然后引发异常。
如果无法处理类型,则 default
必须引发异常。否则,Python 将隐式返回 None
,这在调用者看来是一个合法的值,并且会被序列化
>>> import orjson, json, rapidjson
>>>
def default(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
>>> orjson.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.OPT_STRICT_INTEGER | orjson.OPT_NAIVE_UTC
。
OPT_APPEND_NEWLINE
将 \n
添加到输出中。这是一个方便的优化,用于 dumps(...) + "\n"
模式。 bytes
对象是不可变的,此模式会复制原始内容。
>>> import orjson
>>> orjson.dumps([])
b"[]"
>>> orjson.dumps([], option=orjson.OPT_APPEND_NEWLINE)
b"[]\n"
OPT_INDENT_2
以两个空格缩进进行美化打印输出。这与标准库中的 indent=2
等效。美化打印较慢且输出更大。orjson 是比较库中最快的美化打印库,并且比标准库慢得多。此选项与所有其他选项兼容。
>>> import orjson
>>> orjson.dumps({"a": "b", "c": {"d": True}, "e": [1, 2]})
b'{"a":"b","c":{"d":true},"e":[1,2]}'
>>> orjson.dumps(
{"a": "b", "c": {"d": True}, "e": [1, 2]},
option=orjson.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, datetime
>>> orjson.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0),
)
b'"1970-01-01T00:00:00"'
>>> orjson.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0),
option=orjson.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, datetime, uuid
>>> orjson.dumps(
{uuid.UUID("7202d115-7ff3-4c81-a7c1-2a1f067b1ece"): [1, 2, 3]},
option=orjson.OPT_NON_STR_KEYS,
)
b'{"7202d115-7ff3-4c81-a7c1-2a1f067b1ece":[1,2,3]}'
>>> orjson.dumps(
{datetime.datetime(1970, 1, 1, 0, 0, 0): [1, 2, 3]},
option=orjson.OPT_NON_STR_KEYS | orjson.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.OPT_SORT_KEYS
兼容。如果使用排序,请注意排序是不稳定的,对于重复键将是不可预测的。
>>> import orjson, datetime
>>> orjson.dumps(
{"other": 1, datetime.date(1970, 1, 5): 2, datetime.date(1970, 1, 3): 3},
option=orjson.OPT_NON_STR_KEYS | orjson.OPT_SORT_KEYS
)
b'{"1970-01-03":3,"1970-01-05":2,"other":1}'
此测量序列化589KiB的JSON,包含100个 dict
的 list
,其中每个 dict
都有365个随机排序的 int
键,代表纪元时间戳,以及一个 str
键。在“str keys”中,键在序列化之前被转换为 str
,并且orjson仍指定 option=orjson.OPT_NON_STR_KEYS
(这总是相对较慢)。
库 | str keys(毫秒) | int keys(毫秒) | int keys sorted(毫秒) |
---|---|---|---|
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, datetime
>>> orjson.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, 1),
)
b'"1970-01-01T00:00:00.000001"'
>>> orjson.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, 1),
option=orjson.OPT_OMIT_MICROSECONDS,
)
b'"1970-01-01T00:00:00"'
OPT_PASSTHROUGH_DATACLASS
将 dataclasses.dataclass
实例传递给 default
。这允许自定义它们的输出,但速度较慢。
>>> import orjson, 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.dumps(User("3b1", "asd", "zxc"))
b'{"id":"3b1","name":"asd","password":"zxc"}'
>>> orjson.dumps(User("3b1", "asd", "zxc"), option=orjson.OPT_PASSTHROUGH_DATACLASS)
TypeError: Type is not JSON serializable: User
>>> orjson.dumps(
User("3b1", "asd", "zxc"),
option=orjson.OPT_PASSTHROUGH_DATACLASS,
default=default,
)
b'{"id":"3b1","name":"asd"}'
OPT_PASSTHROUGH_DATETIME
将 datetime.datetime
、datetime.date
和 datetime.time
实例传递给 default
。这允许将日期时间序列化为自定义格式,例如HTTP日期。
>>> import orjson, datetime
>>>
def default(obj):
if isinstance(obj, datetime.datetime):
return obj.strftime("%a, %d %b %Y %H:%M:%S GMT")
raise TypeError
>>> orjson.dumps({"created_at": datetime.datetime(1970, 1, 1)})
b'{"created_at":"1970-01-01T00:00:00"}'
>>> orjson.dumps({"created_at": datetime.datetime(1970, 1, 1)}, option=orjson.OPT_PASSTHROUGH_DATETIME)
TypeError: Type is not JSON serializable: datetime.datetime
>>> orjson.dumps(
{"created_at": datetime.datetime(1970, 1, 1)},
option=orjson.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
>>>
class Secret(str):
pass
def default(obj):
if isinstance(obj, Secret):
return "******"
raise TypeError
>>> orjson.dumps(Secret("zxc"))
b'"zxc"'
>>> orjson.dumps(Secret("zxc"), option=orjson.OPT_PASSTHROUGH_SUBCLASS)
TypeError: Type is not JSON serializable: Secret
>>> orjson.dumps(Secret("zxc"), option=orjson.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
>>> orjson.dumps({"b": 1, "c": 2, "a": 3})
b'{"b":1,"c":2,"a":3}'
>>> orjson.dumps({"b": 1, "c": 2, "a": 3}, option=orjson.OPT_SORT_KEYS)
b'{"a":3,"b":1,"c":2}'
这测量未排序和排序的twitter.json固件的序列化。
库 | 未排序(毫秒) | 排序(毫秒) | 与 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
>>> orjson.dumps({"a": 1, "ä": 2, "A": 3}, option=orjson.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
将 datetime.datetime
实例上的UTC时区序列化为 Z
而不是 +00:00
。
>>> import orjson, datetime, zoneinfo
>>> orjson.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=zoneinfo.ZoneInfo("UTC")),
)
b'"1970-01-01T00:00:00+00:00"'
>>> orjson.dumps(
datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=zoneinfo.ZoneInfo("UTC")),
option=orjson.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 在整个过程中维护一个映射键的缓存。这通过避免重复字符串来减少了内存使用。键的长度最多为 64 字节,以进行缓存,并存储 512 个条目。
在调用期间保持全局解释器锁 (GIL)。
如果给定无效的类型或无效的 JSON,则会引发 JSONDecodeError
。这包括如果输入包含标准库允许,但不是有效 JSON 的 NaN
、Infinity
或 -Infinity
。
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.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, datetime, zoneinfo
>>> orjson.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.dumps(
datetime.datetime(2100, 9, 1, 21, 55, 2).replace(tzinfo=zoneinfo.ZoneInfo("UTC"))
)
b'"2100-09-01T21:55:02+00:00"'
>>> orjson.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, datetime
>>> orjson.dumps(datetime.time(12, 0, 15, 290))
b'"12:00:15.000290"'
datetime.date
对象将始终进行序列化。
>>> import orjson, datetime
>>> orjson.dumps(datetime.date(1900, 1, 2))
b'"1900-01-02"'
与 tzinfo
相关的错误会导致引发 JSONEncodeError
。
与在调用 dumps()
之前进行序列化相比,使用 orjson 序列化 datetime 对象更快。如果使用不支持的类型,如 pendulum.datetime
,请使用 default
。
要禁用 datetime
对象的序列化,指定选项 orjson.OPT_PASSTHROUGH_DATETIME
。
要使用 "Z" 后缀而不是 "+00:00" 来指示 UTC ("Zulu") 时间,使用选项 orjson.OPT_UTC_Z
。
要假定没有时区的 datetime 是 UTC,设置选项 orjson.OPT_NAIVE_UTC
。
enum
orjson 可以原生地序列化枚举。
>>> import enum, datetime, orjson
>>>
class DatetimeEnum(enum.Enum):
EPOCH = datetime.datetime(1970, 1, 1, 0, 0, 0)
>>> orjson.dumps(DatetimeEnum.EPOCH)
b'"1970-01-01T00:00:00"'
>>> orjson.dumps(DatetimeEnum.EPOCH, option=orjson.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.dumps(CustomEnum.ONE, default=default)
b'1'
float
orjson 以无精度损失和一致舍入的方式序列化和反序列化双精度浮点数。在 rapidjson、simplejson 和 json 中观察到相同的行为。ujson 1.35 在序列化和反序列化中都不准确,即它修改了数据,而最近的 2.0 版本是准确的。
orjson.dumps()
将 Nan、Infinity 和 -Infinity 序列化为 null
,它们不符合 JSON 规范。
>>> import orjson, ujson, rapidjson, json
>>> orjson.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
>>> orjson.dumps(9007199254740992)
b'9007199254740992'
>>> orjson.dumps(9007199254740992, option=orjson.OPT_STRICT_INTEGER)
JSONEncodeError: Integer exceeds 53-bit range
>>> orjson.dumps(-9007199254740992, option=orjson.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.OPT_SERIALIZE_NUMPY
。
>>> import orjson, numpy
>>> orjson.dumps(
numpy.array([[1, 2, 3], [4, 5, 6]]),
option=orjson.OPT_SERIALIZE_NUMPY,
)
b'[[1,2,3],[4,5,6]]'
数组必须是一个连续的 C 数组(C_CONTIGUOUS
)和所支持的任何数据类型。
numpy.datetime64
实例以 RFC 3339 字符串序列化,并且日期时间选项会影响它们。
>>> import orjson, numpy
>>> orjson.dumps(
numpy.datetime64("2021-01-01T00:00:00.172"),
option=orjson.OPT_SERIALIZE_NUMPY,
)
b'"2021-01-01T00:00:00.172000"'
>>> orjson.dumps(
numpy.datetime64("2021-01-01T00:00:00.172"),
option=(
orjson.OPT_SERIALIZE_NUMPY |
orjson.OPT_NAIVE_UTC |
orjson.OPT_OMIT_MICROSECONDS
),
)
b'"2021-01-01T00:00:00+00:00"'
如果数组不是一个连续的 C 数组,包含一个支持的数据类型,或者包含一个使用不支持表示(例如,飞秒)的 numpy.datetime64
,orjson 将回退到 default
。在 default
中,可以指定 obj.tolist()
。如果数组格式不正确,这是不期望的,将引发 orjson.JSONEncodeError
。
此措施测量从具有维度 (50000, 100)
和 numpy.float64
值的 numpy.ndarray
中序列化 92MiB 的 JSON。
库 | 延迟(毫秒) | 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.ndarray
中序列化 100MiB 的 JSON,并且具有 numpy.int32
值。
库 | 延迟(毫秒) | 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.ndarray
中序列化 105MiB 的 JSON,并且具有 numpy.bool
值。
库 | 延迟(毫秒) | 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 模块更严格,json 模块将序列化和反序列化无效的 UTF-8 的 UTF-16 代理,例如 "\ud800"。
如果 orjson.dumps()
被一个不包含有效 UTF-8 的 str
给出,将引发 orjson.JSONEncodeError
。如果 loads()
接收无效的 UTF-8,将引发 orjson.JSONDecodeError
。
orjson 和 rapidjson 是唯一在输入错误时持续报错的 JSON 库。
>>> import orjson, ujson, rapidjson, json
>>> orjson.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.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'
为了尽可能地将坏输入反序列化,首先使用 errors
参数的 replace
或 lossy
参数对 bytes
进行解码。
>>> import orjson
>>> orjson.loads(b'"\xed\xa0\x80"')
JSONDecodeError: str is not valid UTF-8: surrogates not allowed
>>> orjson.loads(b'"\xed\xa0\x80"'.decode("utf-8", "replace"))
'���'
uuid
orjson 将 uuid.UUID
实例序列化为 RFC 4122 格式,例如,“f81d4fae-7dec-11d0-a765-00a0c91e6bf6”。
>>> import orjson, uuid
>>> orjson.dumps(uuid.UUID('f81d4fae-7dec-11d0-a765-00a0c91e6bf6'))
b'"f81d4fae-7dec-11d0-a765-00a0c91e6bf6"'
>>> orjson.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 仓库中固定值的测试。它经过测试,不会因 Big List of Naughty Strings 而崩溃。它经过测试,不会泄漏内存。它经过测试,不会因无效的 UTF-8 而崩溃或接受无效的 UTF-8。有一些集成测试锻炼库在 Web 服务器(使用多进程/forked 工作进程的 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
库 | 导入,读取() 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
库 | 导入,读取() 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
库 | 导入,读取() 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
库 | 导入,读取() 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 --no-sdist --release --strip --cargo-extra-args="--features=unstable-simd"
要在稳定渠道构建,不要指定--features=unstable-simd
。
项目自己的CI测试针对nightly-2022-02-13
和稳定1.54。明智的做法是将nightly版本锁定,因为这个渠道可能会引入破坏性更改。
orjson在Linux上针对amd64、aarch64和arm7进行了测试。它在macOS上针对amd64进行了测试,并附带支持aarch64的aarch64 wheel。对于Windows,它在amd64上进行了测试。
除了libc之外,没有其他运行时依赖。
orjson的测试包含在PyPI的源分布中。运行测试的要求在test/requirements.txt
中指定。测试应作为构建的一部分运行。可以使用pytest -q test
运行。
许可证
orjson由ijl ijl@mailbox.org编写,版权所有2018 - 2022,Apache 2和MIT双重许可。
项目详细信息
下载文件
下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。
源代码分发
构建分发
orjson_pydantic-3.6.7.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | e3fa285dada580dde22b52e4d4d4a681e186746e6b0dd082b87c60913a757c39 |
|
MD5 | 573af913ac00adb339c97db9235ab06b |
|
BLAKE2b-256 | a1a7ee32301b4d39f01939e56eca64fc0d0345a888e8f86337045717034cc2eb |
orjson_pydantic-3.6.7-cp310-none-win_amd64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | c6496ebd9b8df5ebdae321caeeab88576c806b080a719b340425c321942d1587 |
|
MD5 | 9fa7c6037c7d4e898f69808218106472 |
|
BLAKE2b-256 | 59e708977cf1637d251c50dd2f6fe2bd00e8d3a66d3939998025c5230ef1a059 |
orjson_pydantic-3.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 8991e5ffccde321bbe1dd2f93bfa7d7a642f738006ebf2a424a3bc7de6f45630 |
|
MD5 | 156fddb846fd82d7635376e1b04d4872 |
|
BLAKE2b-256 | 56c89fa1cf96a97783f96817f1e9c2f3d189c524a6ff475ee850cb031beb6023 |
orjson_pydantic-3.6.7-cp310-cp310-macosx_10_7_x86_64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 020b99fc479b5c6fd42e86601cb6ed6fa127b053f2313e19ed50a5d0b0a0a363 |
|
MD5 | c3264f6584d774538effca3b3422e3ea |
|
BLAKE2b-256 | 3007e23977c3e3cd14246d54a2c9efe0755a1160c5df0f0abad47b97a70ff0d2 |
orjson_pydantic-3.6.7-cp39-none-win_amd64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | db7c762f7cd12ee99c104cc64f2e7df0d2530b315ec495b48db53de31471e993 |
|
MD5 | e57429ddae58cbe7dc5e45fbad7e3ad7 |
|
BLAKE2b-256 | 4ee60f48376993c6480c4a133c73f6341656f89a182e05d58dc6b3460cb7cf1d |
orjson_pydantic-3.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 306e471b00babf2ea208f73fc38b8d9da8f11ff91bac804852c7f09c03925f54 |
|
MD5 | 165038b5f40a993cab40c638dd4520ce |
|
BLAKE2b-256 | dd33cf68d4edf8f3743b747cb39c9c95ed90f1b4dbdb4a4dd9d3405fdf9468bb |
orjson_pydantic-3.6.7-cp39-cp39-macosx_10_7_x86_64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 2d2cf1f5c289287652924d1ef35d3eedc88efc7c005656cd0a1a39a2f0c32f3c |
|
MD5 | 0d03e72145374287fc31cc198685d0ac |
|
BLAKE2b-256 | f6d356aa853ee037bf8417b6a55d68a317b13a2c5255fa979e8f9b30514e5fda |
orjson_pydantic-3.6.7-cp38-none-win_amd64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | f73d5d5b6082a471b8cc7319d11c7dba4e74f2cfcb984e444acad97fc08e8d59 |
|
MD5 | 4c5d47d5b8c65e781b6ba433aa4d912a |
|
BLAKE2b-256 | 5b00188db53798661f5e81df3ab62f7fd6b6bfe8eeef5070c191dab4f5dcfeaf |
哈希值 for orjson_pydantic-3.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 0458e9626a9a5167b48caa85aa74f98d89c791c13421589f3da95bf9f3237b7f |
|
MD5 | 4440cec33845aa7a739e73657fb2f308 |
|
BLAKE2b-256 | e3df2ad7ef34d3b0e1e7a7d9a07a37d022577d3fe6737dfb69fed993c30e6c0f |
哈希值 for orjson_pydantic-3.6.7-cp38-cp38-macosx_10_7_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | d6db4df1dc918be1b372e0f5967385e30849e6a7b5e8f61a5ca45b7d5e21542a |
|
MD5 | 830e86fc1905330e68bbc18e78a3eb1f |
|
BLAKE2b-256 | 9a3b6f0210fcd559c20a7bc0ddaea656a042822ba3a7a8892950aebc41ddf2f1 |
哈希值 for orjson_pydantic-3.6.7-cp37-none-win_amd64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 8ab178603be15eabfe7814413d593bdae141fe7f2577ab4bfbae46a8c794460f |
|
MD5 | 4f4f9204f0df09e5963bf49518a86abd |
|
BLAKE2b-256 | 3145f7317e4f69141c7cea682d5a8dc72335618a87f5977073c3e3dbe7727c34 |
哈希值 for orjson_pydantic-3.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | dd4e900f9ad0f9471cc6fcf902f5b201e44631379e78c9cb2cde1798ba558c67 |
|
MD5 | 7937bb0d62dcceac7d34858481b25194 |
|
BLAKE2b-256 | 40b73cd70a5d5a28aa8a6f32bed484edaea1b3e5dfb07218bd72df47c686dc1f |
哈希值 for orjson_pydantic-3.6.7-cp37-cp37m-macosx_10_7_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 8cd8fdd80e7ccd9d4cdb5e5c7e7142d945d0424c08ac74a2205db095068abe59 |
|
MD5 | 1c85c2962f2a76441fbd594f7903764f |
|
BLAKE2b-256 | 1c5c2629c018bf177e08a9aeb0968c541b81ed8ca3a60ea1923dd26a6f01e692 |