一个更快、支持对更多数据类型进行序列化、反序列化的Python库

一个快速正确的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、要让自己变得更强:想想,假如你是要在测试这个行业长期做下去,你的工作经验和测试技术是绝对不够的,你需要提升,你需要丰富你的技术栈!还等什么!


这一些资料,对做【软件测试】的朋友而言应该是较为完整了,这类学习资料也陪伴我走过了最艰难的路程,希望也可以帮助到你!万事要尽早,尤其是技术行业,一定要提升技术功底。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容