跳转到主要内容

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

项目描述

orjson-pydantic

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

orjson是一个快速、准确的Python JSON库。它被认为是Python中最快的JSON库之一,并且比标准json库或其他第三方库更准确。它可以序列化 性能dataclassdatetimenumpyUUID 实例。

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

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

  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_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序列化更多类型。现在序列化strintdictlist的子类。这更快,更接近标准库。可以通过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_KEYSsort_keys被替换为option=orjson_pydantic.OPT_SORT_KEYSindent被替换为option=orjson_pydantic.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_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

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

默认

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

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

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

接受 bytesbytearraymemoryviewstr 输入。如果输入作为 memoryviewbytearraybytes 对象存在,建议直接传递这些对象,而不是创建不必要的 str 对象。这具有更低的内存使用和更低的延迟。

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

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

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

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

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_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 支持具有 tzinfoNonedatetime.timezone.utc、python3.9+ zoneinfo 模块的时区实例或来自第三方库 pendulumpytzdateutil/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.float64numpy.float32numpy.int64numpy.int32numpy.int8numpy.uint64numpy.uint32numpy.uint8numpy.uintpnumpy.intpnumpy.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'

为了尽可能地在反序列化错误输入时成功,首先使用 replacelossy 参数的 errorsbytes 进行解码。

>>> 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() 不一样

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

测试

该库具有全面的测试。它对JSONTestSuitenativejson-benchmark存储库中的固定值进行了测试。它经过测试,不会崩溃于恶毒字符串大列表。它经过测试,不会泄漏内存。它经过测试,不会崩溃并且不接受无效的UTF-8。它还进行了集成测试,以检验在Web服务器(使用多进程/分叉工作进程的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

导入,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进行测量的。

可以使用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 --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 (552.4 kB 查看哈希值)

上传时间

构建分布

orjson_pydantic2-3.6.7-cp310-none-win_amd64.whl (194.4 kB 查看哈希值)

上传时间 CPython 3.10 Windows x86-64

orjson_pydantic2-3.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (265.4 kB 查看哈希值)

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

orjson_pydantic2-3.6.7-cp310-cp310-macosx_10_7_x86_64.whl (247.7 kB 查看哈希值)

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

orjson_pydantic2-3.6.7-cp39-none-win_amd64.whl (194.4 kB 查看哈希值)

上传时间 CPython 3.9 Windows x86-64

orjson_pydantic2-3.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (265.4 kB 查看哈希值)

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

orjson_pydantic2-3.6.7-cp39-cp39-macosx_10_7_x86_64.whl (247.7 kB 查看哈希值)

上传于 CPython 3.9 macOS 10.7+ x86-64

orjson_pydantic2-3.6.7-cp38-none-win_amd64.whl (194.3 kB 查看哈希值)

上传于 CPython 3.8 Windows x86-64

orjson_pydantic2-3.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (265.3 kB 查看哈希值)

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

orjson_pydantic2-3.6.7-cp38-cp38-macosx_10_7_x86_64.whl (247.6 kB 查看哈希值)

上传于 CPython 3.8 macOS 10.7+ x86-64

orjson_pydantic2-3.6.7-cp37-none-win_amd64.whl (194.3 kB 查看哈希值)

上传于 CPython 3.7 Windows x86-64

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

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

orjson_pydantic2-3.6.7-cp37-cp37m-macosx_10_7_x86_64.whl (247.7 kB 查看哈希值)

上传于 CPython 3.7m macOS 10.7+ x86-64

由以下机构支持