一个快速正确的Python JSON库,它本身就支持数据类、日期、时间、numpy 数据类型。
最近,在开发中遇到需要对大量复杂JSON进行序列化和反序列化操作场景,但发现内置的json库速度非常慢,想使用第三方库替代它,于是发现了上面提到的 orjson 库。
下面,让我们看一下orjson 与其他Python json库相比的优缺点。
序列化数据类(dataclasses.dataclas)实例的速度是其他库的40-50倍。
将datetime、date和time实例序列化为RFC 3339格式,例如,“1970-01-01T00:00:00+00:00”。
序列化 numpy.ndarray的实例化速度是其他库的0.3倍。
序列化为字节而不是字符串,也就是说,不是临时替换。
序列化字符串而不将unicode转义为ASCII,例如,"好"而不是"\u597d"。
序列化浮点型数据的速度是其他库的10倍,反序列化的速度是其他库的两倍。
具有严格的UTF-8和JSON格式一致性,比标准库更正确。
不额外提供用于读取/写入类文件对象的load()或dump()函数。
安装
使用pip 直接安装,如下:
pip install orjson
简单示例
下面是一个含序列化、反序列化的示例:
data = {"emoji": "", "integer": 9527, "float": 9.527, "boolean": False,
"list": ["", 9527, 9.527, False],
"dict": {"key1": "value1", "key2": "value2"},
"chinese": "您好", "japanese": "こんにちは",
"created_at": datetime.datetime(1970, 1, 1),
"status": "🆗", "payload": numpy.array([[1, 2], [3, 4]])}
# 序列化 data
data_dumps = orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY)
print(data_dumps)
# 反序列化 data_dumps
data_loads = orjson.loads(data_dumps)
print(data_loads)
# 执行上述代码,输出结果为:
b'{"emoji":"\xf0\x9f\x98\x82","integer":9527,"float":9.527,"boolean":false,"list":["\xf0\x9f\x98\x82",9527,9.527,false],"dict":{"key1":"value1","key2":"value2"},"chinese":"\xe6\x82\xa8\xe5\xa5\xbd","japanese":"\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf","created_at":"1970-01-01T00:00:00+00:00","status":"\xc3\xb0\xc5\xb8\xe2\x80\xa0\xe2\x80\x94","payload":[[1,2],[3,4]]}'
{'emoji': '', 'integer': 9527, 'float': 9.527, 'boolean': False, 'list': ['', 9527, 9.527, False], 'dict': {'key1': 'value1', 'key2': 'value2'}, 'chinese': '您好', 'japanese': 'こんにちは', 'created_at': '1970-01-01T00:00:00+00:00', 'status': '🆗', 'payload': [[1, 2], [3, 4]]}
因头条显示问题,部分数据显示不出,特附上述代码截图,如下:
序列化
参数说明
对于序列化,可以指定以下两个输入参数:
*default:要序列化子类或任意类型,可指定default作为返回受支持类型的可调用对象。 此外,您可以通过引发诸如TypeError之类的异常来强制执行规则以处理不受支持的日期类型。
option:若要修改数据序列化的方式,请指定选项。在orjson 中,每个选项都是一个整数常量。要指定多个选项,将它们一起屏蔽,例如: option=orjson.OPT_STRICT_INTEGER | orjson.OPT_NAIVE_UTC 。
序列化 default 参数
当输入包含不支持的Decimal数据类型时,则会引发错误。
import decimal
orjson.dumps(decimal.Decimal("3.141592653"))
运行上述代码,将得到以下输出:
TypeError: Type is not JSON serializable: decimal.Decimal
为了使得 orjson 序列化支持Decimal数据类型,我们可以创建一个 callable 函数或lambda 表达式并将其作为default参数传递,如下。
def default(obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
raise TypeError
data = orjson.dumps(decimal.Decimal("0.0842389659712649442845"), default=default)
# 执行上述代码,输出结果为:
b'"0.0842389659712649442845"'
序列化 option 参数
OPT_APPEND_NEWLINE :在输出中附加\n 。
OPT_INDENT_2 :缩进两个空格的打印输出。
OPT_NAIVE_UTC :将没有tzinfo的 datetime.datetime对象序列化为UTC。 这对设置了tzinfo datetime.datetime对象没有影响。
OPT_NON_STR_KEYS :序列化字符串以外类型的字典键。它允许dict的键为字符串、整型、浮点型、布尔型、None、时间(datetime.time、datetime.datetime)、日期(datetime.date)、枚举(enum.Enum)、uuid.UUID。
OPT_OMIT_MICROSECONDS :不要序列化datetime.datetime或datetime.time实例上的微秒级数据。
OPT_PASSTHROUGH_DATACLASS: 支持序列化数据类(dataclasses.dataclas)实例时,通过default参数 定制化输出内容。
OPT_PASSTHROUGH_DATETIME: 序列化datetime.datetime, datetime.date, and datetime.time实例时,通过default参数自定义格式。
OPT_SERIALIZE_NUMPY :序列化numpy.ndarray实例。
OPT_SORT_KEYS :按排序顺序对字典键进行序列化。 默认值是未指定顺序进行序列化。
OPT_STRICT_INTEGER :对整数(而不是标准的64位)强制执行53位限制。
代码示例,如下:
import orjson, datetime, uuid
# 序列化 dict键为uuid.UUID的数据
orjson.dumps(
{uuid.UUID("9527d115-6ff8-9aj1-n3b1-128sj384392135reiop"): [1, 2]},
option=orjson.OPT_NON_STR_KEYS,
)
# 序列化 dict键为datetime.datetime的数据
orjson.dumps(
{datetime.datetime(2021, 1, 1, 0, 0, 0): [1, 2]},
option=orjson.OPT_NON_STR_KEYS | orjson.OPT_NAIVE_UTC,
)
# 不序列化微妙字段
orjson.dumps(
datetime.datetime(2021, 1, 1, 0, 0, 0, 1),
option=orjson.OPT_OMIT_MICROSECONDS,
)
# 序列化 dataclasses 数据时,通过default参数定制化输出内容
import dataclasses 数据
@dataclasses.dataclass
class User:
id: str
name: str
password: str
def default(obj):
if isinstance(obj, User):
return { "name": obj.name,"password":obj.password}
raise TypeError
orjson.dumps(
User("123", "Tom", "123456"),
option=orjson.OPT_PASSTHROUGH_DATACLASS,
default=default,
)
# 序列化datetime.datetime实例时,通过default参数自定义格式。
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(2021, 1, 1)},
option=orjson.OPT_PASSTHROUGH_DATETIME,
default=default,
)
反序列化
loads() 接受bytes、bytearray、memoryview、STR输入。它反序列化为dict、list、int、float、str、bool和None对象。如果输入作为memoryview、bytearray或bytes对象存在,建议直接传递这些,而不是创建一个不必要的str对象。这样可以降低内存使用量和延迟,输入必须是有效的UTF-8。
文件读/写
通常,我们可以通过write() 函数将序列化后返回的字节数据保存到文件中。 但是,需要在mode参数中包含b模式 。
data = {"emoji": "", "integer": 9527, "float": 9.527, "boolean": False,
"list": ["", 9527, 9.527, False],
"dict": {"key1": "value1", "key2": "value2"},
"chinese": "您好", "japanese": "こんにちは",
"created_at": datetime.datetime(1970, 1, 1),
"status": "🆗", "payload": numpy.array([[1, 2], [3, 4]])
}
with open("example.json", "wb") as f:
f.write(orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY))
因头条显示问题,部分数据显示不出,特附上述代码截图,如下:
生成的example.json 内容如下:
同样,从文件中读取数据也很简单,如下所示:
with open("example.json", "rb") as f:
json_data = orjson.loads(f.read())
print(json_data)
# 执行上述代码,输出结果为
{'emoji': '', 'integer': 9527, 'float': 9.527, 'boolean': False, 'list': ['', 9527, 9.527, False], 'dict': {'key1': 'value1', 'key2': 'value2'}, 'chinese': '您好', 'japanese': 'こんにちは', 'created_at': '1970-01-01T00:00:00+00:00', 'status': '🆗', 'payload': [[1, 2], [3, 4]]}
最后,性能测试
我们通过简单的测试,来比较一下json、ujson、orjson三者的序列化性能,其中json库为Pthon内置库,ujson 库是用 C实现的,orjson 库是用Rust 实现的。
# -*- coding: utf-8 -*-
import json
import random
import ujson
import orjson
import time
def cost_time(func):
def inner(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
stop_time = time.time()
print("{0} 耗时:{1}".format(func.__name__, stop_time - start_time))
return result
return inner
@cost_time
def json_dumps(obj):
return json.dumps(obj)
@cost_time
def ujson_dumps(obj):
return ujson.dumps(obj)
@cost_time
def orjson_dumps(obj):
return orjson.dumps(obj)
if __name__ == '__main__':
test = {}
for i in range(1, 2000000):
test[str(i)] = ''.join(random.sample(
['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm',
'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e','d', 'c', 'b', 'a'], 10))
json_dumps(test)
ujson_dumps(test)
orjson_dumps(test)
我们可以看到,同样是序列含200万k-v 的dict对象,使用orjson的处理性能远比其他两个库效率高的多。
json_dumps 耗时:1.1578669548034668
ujson_dumps 耗时:0.45979905128479004
orjson_dumps 耗时:0.09074163436889648
看了这篇内容后,坚信以下两件事,也会对你的自我提升有一定的帮助:
1、点赞,让更多人能看到,同时你的认可也会鼓励我创作更多优质内容。
2、要让自己变得更强:想想,假如你是要在测试这个行业长期做下去,你的工作经验和测试技术是绝对不够的,你需要提升,你需要丰富你的技术栈!还等什么!
这一些资料,对做【软件测试】的朋友而言应该是较为完整了,这类学习资料也陪伴我走过了最艰难的路程,希望也可以帮助到你!万事要尽早,尤其是技术行业,一定要提升技术功底。