orjson:功能丰富的高性能 Python JSON 库

orjson是一个JSON库,它可以快速准确地完成Python对象和JSON格式的相互转换,相较于P
首页 新闻资讯 行业资讯 orjson:功能丰富的高性能 Python JSON 库

24e4dc25333302de5fb38248c984a55e67bbcc.jpg

简介

首先我们先来了解下orjson的优缺点:

  • 可以将datetime、date和time实例序列化为RFC 3339格式,例如:"2022-06-12T00:00:00+00:00"

  • 序列化numpy.ndarray实例的速度比其他库快4-12倍,但使用的内存更少,约为其他库的1/3左右

  • 输出速度是标准库的10到20倍

  • 序列化的结果是bytes类型,而不是str

  • 序列化str时,不会将unicode转义为ASCII

  • 序列化float的速度是其他库的10倍,反序列化的速度是其他库的两倍

  • 可以直接序列化str、int、list和dict的子类

  • 不提供load( )和dump( )方法,在原生JSON库中,load( )方法可以把json格式的文件转换成python对象

  1. 序列化dataclass类型

复制

import dataclasses, orjson, typing@dataclasses.dataclassclass Member:   id: int
   active: bool = dataclasses.field(default=False)@dataclasses.dataclassclass Object:   id: int
   name: str
   members: typing.List[Member]print(orjson.dumps(Object(1, "a", [Member(1, True), Member(2)])))
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

输出为:b'{"id":1,"name":"a","members":[{"id":1,"active":true},{"id":2,"active":false}]}'

  1. 序列化 float

orjson序列化和反序列化双精度浮点数,不会损失精度。当序列化NaN,Infinity,-Infinity时,会返回null。

复制

>>> 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]'
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  1. 序列化Int类型

orjson可以对整数进行序列化和反序列化。但web浏览器只支持53-bit的整数,当值超过53-bit时会产生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
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  1. 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]]'
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  1. str

orjson只处理UTF-8格式的字符串,如果给orjson.dumps()方法传入一个UTF-16的字符串,会产生报错。

复制

>>> import orjson>>> orjson.dumps('\ud800')JSONEncodeError: str is not valid UTF-8: surrogates not allowed
  • 1.

  • 2.

  • 3.

  1. uuid

orjson可以把uuid.UUID实例序列化为RFC 4122格式。

复制

>>> 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"'
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

安装

orjson支持3.7-3.10所有版本64位的Python,注意32位的 Python 无法使用orjson!本文将在3.8.2环境下使用orjson,使用以下命令安装orjson:

复制

pip install --upgrade "pip>=20.3"pip install --upgrade orjson
  • 1.

  • 2.

使用

  1. 基本使用

我们首先使用orjson序列化一个字典,随后再将结果反序列化:

复制

import orjson, datetime, numpydata = {   "type": "job",   "created_at": datetime.datetime(2022, 6, 12),   "status": "🆗",   "payload": numpy.array([[1, 2], [3, 4]]),
}# 把python类型的数据转换成json形式,结果是bytes类型,而不是strserialize = orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY)print(serialize)# 将序列化的结果转换为python数据deserialize = orjson.loads(serialize)print(deserialize)
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

输出结果为:

复制

b'{"type":"job","created_at":"2022-06-12T00:00:00+00:00","status":"\xf0\x9f\x86\x97","payload":[[1,2],[3,4]]}'{'type': 'job', 'created_at': '2022-06-12T00:00:00+00:00', 'status': '🆗', 'payload': [[1, 2], [3, 4]]}
  • 1.

  • 2.

  1. 序列化

dumps( )可以将Python对象序列化为JSON数据,但与原生JSON库不同的是,orjson.dumps( )得到的JSON数据是bytes类型,少了将bytes转换为str的操作,因此,速度会大大加快。下面我们详细介绍下dumps( ):

复制

def dumps(   __obj: Any,   default: Optional[Callable[[Any], Any]] = ...,   option: Optional[int] = ...,
) -> bytes: ...
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

(1) default 参数

我们先用dumps( )序列化一个Decimal类型的数据:

复制

import orjson, decimalorjson.dumps(decimal.Decimal("0.0842389659712649442845"))
  • 1.

  • 2.

会得到无法序列化Decimal类型的错误:

复制

TypeError: Type is not JSON serializable: decimal.Decimal
  • 1.

这个时候,我们可以创建一个函数,将其作为default参数传递,来对Decimal进行序列化:

复制

import orjson, decimaldef default(obj):   if isinstance(obj, decimal.Decimal):       return str(obj)   raise TypeErrorres = orjson.dumps(decimal.Decimal("0.0842389659712649442845"), default=default)print(res)
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

结果为:

复制

b'"0.0842389659712649442845"'
  • 1.

(2) option参数

  • 使用option参数可以定制序列化的结果。假如我们想在输出后面加上一个\n,可以使用OPT_APPEND_NEWLINE

复制

import orjsonprint(orjson.dumps([], option=orjson.OPT_APPEND_NEWLINE))
  • 1.

  • 2.

结果为:b'[]\n'

  • OPT_OMIT_MICROSECONDS可以设置datetime.datetime实例的序列化结果没有微秒

复制

import orjson, datetimeprint(orjson.dumps(       datetime.datetime(2022, 6, 12, 0, 0, 0, 1),       option=orjson.OPT_OMIT_MICROSECONDS,
   ))
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

输出为:b'"2022-06-12T00:00:00"'

  • 使用OPT_PASSTHROUGH_DATACLASS,可以定制化输出结果,假如数据中包含用户密码,我们需要在序列化后隐藏密码,可以这样做:

复制

import orjson, dataclasses@dataclasses.dataclassclass User:   id: str
   name: str
   password: strdef default(obj):   if isinstance(obj, User):       return {"id": obj.id, "name": obj.name}   raise TypeErrorprint(orjson.dumps(       User("3b1", "asd", "zxc"),       option=orjson.OPT_PASSTHROUGH_DATACLASS,       default=default,
   ))
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

输出的结果中就没有用户密码了:b'{"id":"3b1","name":"asd"}'

  • OPT_PASSTHROUGH_DATETIME可以将日期格式化输出:

复制

import orjson, datetimedef default(obj):   if isinstance(obj, datetime.datetime):       return obj.strftime("%a, %d %b %Y %H:%M:%S GMT")   raise TypeErrorprint(orjson.dumps(
       {"创建时间": datetime.datetime(1970, 1, 1)},       option=orjson.OPT_PASSTHROUGH_DATETIME,       default=default,
   ))
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

输出为:b'{"创建时间":"Thu, 01 Jan 1970 00:00:00 GMT"}'

  • OPT_SORT_KEYS可以将序列化结果的键值按顺序排列

复制

import orjsonprint(orjson.dumps({"b": 1, "c": 2, "a": 3}, option=orjson.OPT_SORT_KEYS))
  • 1.

  • 2.

输出为:b'{"a":3,"b":1,"c":2}'

  1. 反序列化

loads()可以将JSON数据转换为Python对象,该方法支持多种数据类型,包括:bytes, bytearray, memoryview, 和 str。

  1. 性能分析

我们用orjson, ujson, simplejson, json分别对一个列表进行序列化,该列表中有1000000个元素:

复制

import orjson,timeimport randomstart = time.time()data = [{   'id': 1,   'value': random.uniform(0,2000)
}for i in range(1000000)]orjson.dumps(data)end = time.time()print("总共耗时 " + str(round(end-start, 2)) + ' s')
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

每个库的耗时如下表所示:

耗时(s)

orjson

0.78

ujson

1.85

simplejson

2.84

json

2.21

由此可以看出,orjson的效率更高。

后记

在日常的开发工作中,我们经常需要将一些数据存储为JSON格式,最常用的就是Python原生的JSON库,但是该库速度较慢, 当数据量过大时,使用不便。而orjson的功能强大,它支持多种类型的数据序列化,开发者还可以根据需要定制化输出, 与其他第三方JSON库相比,效率更高。

20    2022-06-29 08:55:46    orjson Python JSON