跳转到主要内容

快速、准确的Python JSON库,支持dataclasses、datetimes和numpy

项目描述

orjson-pydantic

这是orjson的一个(维护的)分支,它增加了pydantic对象的序列化。

orjson是一个快速、准确的Python JSON库。它是基准测试最快的Python JSON库,并且比标准json库或其他第三方库更准确。它可以原生序列化dataclassdatetimenumpyUUID实例。

与其他Python JSON库相比的特点和缺点

  • 序列化dataclass实例比其他库快40-50倍
  • datetimedatetime实例序列化为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 倍,反序列化的速度是其他库的两倍
  • 可以原生序列化 strintlistdict 的子类,需要通过 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

  1. 用法
    1. 安装
    2. 快速入门
    3. 迁移
    4. 序列化
      1. 默认
      2. 选项
    5. 反序列化
  2. 类型
    1. dataclass
    2. datetime
    3. enum
    4. float
    5. int
    6. numpy
    7. str
    8. uuid
    9. pydantic
  3. 测试
  4. 性能
    1. 延迟
    2. 内存
    3. 重现
  5. 问题
  6. 打包
  7. 许可证

用法

安装

要从 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 序列化更多类型。现在序列化了 strintdictlist 的子类。这更快,更接近标准库。可以通过 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。

它原生序列化 strdictlisttupleintfloatbooldataclasses.dataclasstyping.TypedDictdatetime.datetimedatetime.datedatetime.timeuuid.UUIDnumpy.ndarraypydantic.BaseModelNone 实例。它通过 default 支持任意类型。它序列化 strintdictlistdataclasses.dataclassenum.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

JSONEncodeErrorTypeError 的子类。这是为了与标准库兼容。

默认

要序列化子类或任意类型,请指定 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。每个 optionorjson 中的整型常量。要指定多个选项,将它们掩码在一起,例如,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

将没有 tzinfodatetime.datetime 对象序列化为 UTC。这对已设置 tzinfodatetime.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

序列化类型为非 strdict 键。这允许 dict 键为以下类型之一:strintfloatboolNonedatetime.datetimedatetime.datedatetime.timeenum.Enumuuid.UUID。相比之下,标准库默认序列化 strintfloatboolNone。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个 dictlist,其中每个 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.datetimedatetime.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.datetimedatetime.datedatetime.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对象。它反序列化为 dictlistintfloatstrboolNone 对象。

接受 bytesbytearraymemoryviewstr 输入。如果输入是 memoryviewbytearraybytes 对象,建议直接传递这些对象,而不是创建不必要的 str 对象。这样可以降低内存使用量和延迟。

输入必须是有效的 UTF-8。

orjson 在整个过程中维护一个映射键的缓存。这通过避免重复字符串来减少了内存使用。键的长度最多为 64 字节,以进行缓存,并存储 512 个条目。

在调用期间保持全局解释器锁 (GIL)。

如果给定无效的类型或无效的 JSON,则会引发 JSONDecodeError。这包括如果输入包含标准库允许,但不是有效 JSON 的 NaNInfinity-Infinity

JSONDecodeErrorjson.JSONDecodeErrorValueError 的子类。这是为了与标准库兼容。

类型

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 实例的实例:Nonedatetime.timezone.utc、python3.9+ zoneinfo 模块中的时区实例,或来自第三方 pendulumpytzdateutil/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.float64numpy.float32numpy.int64numpy.int32numpy.int8numpy.uint64numpy.uint32numpy.uint8numpy.uintpnumpy.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 参数的 replacelossy 参数对 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() 不一样

  1. 它不尊重任何 Config 属性。
  2. 它没有 Pydantic 的任何附加功能(json_encoder、exclusions、inclusions 等)。

测试

库有全面的测试。有针对 JSONTestSuitenativejson-benchmark 仓库中固定值的测试。它经过测试,不会因 Big List of Naughty Strings 而崩溃。它经过测试,不会泄漏内存。它经过测试,不会因无效的 UTF-8 而崩溃或接受无效的 UTF-8。有一些集成测试锻炼库在 Web 服务器(使用多进程/forked 工作进程的 gunicorn)和多线程时的使用。它还使用来自 ultrajson 库的一些测试。

orjson是所有比较过的库中最正确的。此图表显示了每个库如何处理来自JSONTestSuitenativejson-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格式坐标,包含浮点数和数组,缩进。

延迟

alt text alt text alt text alt text alt text alt text alt text alt text

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时测量的。

延迟结果可以使用pybenchgraph脚本重新生成。内存结果可以使用pymem脚本重新生成。

问题

为什么我不能从PyPI安装它?

可能需要将pip升级到版本20.3或更高版本,以支持最新的manylinux_x_y或universal2 wheel格式。

它是否可以将数据反序列化为dataclasses、UUIDs、decimals等,或支持object_hook?

不。这需要一个指定预期类型及其错误处理等的模式。这可以通过高于此级别的数据验证库来解决。

它是否可以将序列化为str

不。bytes是序列化blob的正确类型。

它将支持PyPy吗?

如果有人正确实现它的话。

打包

要打包orjson,需要Rustmaturin构建工具。

这是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 (551.9 kB 查看哈希值)

上传时间 源代码

构建分发

orjson_pydantic-3.6.7-cp310-none-win_amd64.whl (186.6 kB 查看哈希值)

上传时间 CPython 3.10 Windows x86-64

orjson_pydantic-3.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (254.5 kB 查看哈希值)

上传时间 CPython 3.10 manylinux: glibc 2.17+ x86-64

orjson_pydantic-3.6.7-cp310-cp310-macosx_10_7_x86_64.whl (241.2 kB 查看哈希值)

上传时间 CPython 3.10 macOS 10.7+ x86-64

orjson_pydantic-3.6.7-cp39-none-win_amd64.whl (186.6 kB 查看哈希值)

上传时间 CPython 3.9 Windows x86-64

orjson_pydantic-3.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (254.5 kB 查看哈希值)

上传时间 CPython 3.9 manylinux: glibc 2.17+ x86-64

orjson_pydantic-3.6.7-cp39-cp39-macosx_10_7_x86_64.whl (241.2 kB 查看哈希值)

上传时间 CPython 3.9 macOS 10.7+ x86-64

orjson_pydantic-3.6.7-cp38-none-win_amd64.whl (186.4 kB 查看哈希值)

上传时间 CPython 3.8 Windows x86-64

orjson_pydantic-3.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (254.5 kB 查看哈希值)

上传时间 CPython 3.8 manylinux: glibc 2.17+ x86-64

orjson_pydantic-3.6.7-cp38-cp38-macosx_10_7_x86_64.whl (241.0 kB 查看哈希值)

上传时间 CPython 3.8 macOS 10.7+ x86-64

orjson_pydantic-3.6.7-cp37-none-win_amd64.whl (186.5 kB 查看哈希值)

上传时间 CPython 3.7 Windows x86-64

orjson_pydantic-3.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (254.6 kB 查看哈希值)

上传时间 CPython 3.7m manylinux: glibc 2.17+ x86-64

orjson_pydantic-3.6.7-cp37-cp37m-macosx_10_7_x86_64.whl (241.1 kB 查看哈希值)

上传时间 CPython 3.7m macOS 10.7+ x86-64

支持者