>>> from env_helper import info; info()
页面更新时间: 2024-03-14 20:49:52
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

4.9. 序列化的另一个不错的选择——JSON

JSON ( JavaScript Object Notation)是一种轻量级数据交换格式,它基于JavaScript编程 语言的一个子集,于1999年12月成为一个完全独立于语言的文本格式。 由于其格式使用了 其他许多流行编程的约定,如C、C++v c #、Java、JS、Python等,加之其简单灵活、可读性和互操作性较强、易于解析和使用等特点,逐渐变得流行起来,甚至有代替XML的趋 势。 关于JSON和XML之间的优劣,一直有很多争论。此处不对这两者之间的是是非非做分析(笔者的观点是两者各有所长,在相当长的时间里还会共存),这里关注的是JSON用于序列化方面的优势。

在进行详细讨论之前,我们先来看看Python语言中对 JSON的支持现状。

Python中有一系列的模块提供对JSON格式的支持,如simplejson、cjson、yajl、ujson,自 Python2.6后又引入了标准库JSON。 简单来说cjson和ujson是用C来实现的,速度较快。 据 cjson的文挡表述:其速率比纯Python实现的json模块大概要快250倍。 yajl是Cpython版本的 JSON实现,而simplejson和标准库JS0N本质来说无多大区别,实际上Python2.6中的json模块就是simplejson减去对Python2.4、2,5的支持以充分利用最新的兼容未来的功能。 不过相对 于simplejson,标准库更新相对较慢. Python2.7.5中simplejson对应的版本为2.0.9,而最新的 sirnplejson的版本为3.3.0。

在实际应用过程中将这两者结合较好的做法是采用如下import方法:

try:
    import simplejson as json
except ImportError:
    import json

本节仍采用标准库JSON来做一些探讨a Python的标准库JSON提供的最常用的方法 与pickle类似,dump/dumps用来序列化,load/loads用来反序列化。需要注意的json默认不 支持非ASCII-based的编码,如load方法可能在处埋中文字符时不能正常显示,则需要通过 encoding参数指定对应的宇符编码。在序列化方面,相比pickle, JSON具有以下优势:

  1. 使用简单,支持多种数据类型JSON文挡的构成非常简单,仅存在以下两大数据结构

  • 名称/值对的集合。在各种语言中,它被实现为一个对象、记录、结构、字典、散列 表、键列表或关联数组。

  • 值的有序列表。在大多数语言中,它被实现为数组、向量、列表或序列。在Python 中对应支持的数据类型包括字典、列表、字符串、整数、浮点数、True、False、None 等。JSON中数据结构和Python中的转换并不是完全一一对应,存在一定的差异,读者可以自行査阅文档。Python中一个JSON文档可以分解为如图4-3所示形式。

  1. 存储格式可读性更为友好.容易修改。相比于pickle来说,json的格式更加接近程序 员的思维,修改和阅读上要容易得多3 dumpsO函数提供了一个参数indent使生成的jscm文件 可读性更好.0意味着“每个值单独一行”;大于〇的数字意味着“每个值单独一行并且使用 这个数字的空格来缩进嵌套的数据结构”。但需要注意的是,这个参数是以文件大小变大为代 价的。如图44展示的是这两种格式之间的对比,其中json.dumps()使用了 indeiu参数输出。

  2. json支持跨平台跨语言操作,能够轻易被其他语言解析.如Python中生成的json文 件可以轻易使用JavaScript解析,互操作性更强,而pickle格式的文件只能在Python语言中 支持。此外json原生的JavaScript支持,客户端浏览器不需要为此使用额外的解释器,特別 适用于Web应用提供快速、紧凑、方便的序列化操作。此外,相比于pickle, json的茌储格 式更为紧凑,所占空间更小。

_images/45-1.png
图4-4 pickle和json文件格式对比

4.9.1. 具有较强的扩展性

json模块还提供了编码(JSONEncoder)和解码类(JSONDecoder) 以便用户对其默认不支持的序列化类型进行扩展。来看一个例子:

>>> import datetime
>>> d=datetime.datetime.now()
>>> d
datetime.datetime(2024, 3, 14, 20, 49, 52, 454770)
>>> import json
>>> json.dumps(d)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In [6], line 2
      1 import json
----> 2 json.dumps(d)


File /usr/lib/python3.11/json/__init__.py:231, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    226 # cached encoder
    227 if (not skipkeys and ensure_ascii and
    228     check_circular and allow_nan and
    229     cls is None and indent is None and separators is None and
    230     default is None and not sort_keys and not kw):
--> 231     return _default_encoder.encode(obj)
    232 if cls is None:
    233     cls = JSONEncoder


File /usr/lib/python3.11/json/encoder.py:200, in JSONEncoder.encode(self, o)
    196         return encode_basestring(o)
    197 # This doesn't pass the iterator directly to ''.join() because the
    198 # exceptions aren't as detailed.  The list call should be roughly
    199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
    201 if not isinstance(chunks, (list, tuple)):
    202     chunks = list(chunks)


File /usr/lib/python3.11/json/encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
    253 else:
    254     _iterencode = _make_iterencode(
    255         markers, self.default, _encoder, self.indent, floatstr,
    256         self.key_separator, self.item_separator, self.sort_keys,
    257         self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)


File /usr/lib/python3.11/json/encoder.py:180, in JSONEncoder.default(self, o)
    161 def default(self, o):
    162     """Implement this method in a subclass such that it returns
    163     a serializable object for ``o``, or calls the base implementation
    164     (to raise a ``TypeError``).
   (...)
    178
    179     """
--> 180     raise TypeError(f'Object of type {o.__class__.__name__} '
    181                     f'is not JSON serializable')


TypeError: Object of type datetime is not JSON serializable

json在序列化datetime的时候会抛出TypeError异常,这是因为 Json 模块本身不支持 dateline的序列化,因此需要对json本身的JSONEncoder进行扩展。有多种方法可以实现,下面的例子是其中实现之一。

>>> import datetime
>>> from time import mktime
>>> try:
>>>     import simplejson as json
>>> except ImportError: import json
>>> class DateTiraeEncoder (json.JSONEncoder) :     # 对 JSONEnccKier 进行iT.展
>>>     def default(self,obj):
>>>         if isinstance(obj, datetime.datetime):
>>>             return obj.strftime('%Y-%m-%d %H:%M:%S')
>>>         elif isinstance(obj, date):
>>>             return json.JSONEncoder.default(self,obj)
>>> d=datetime.datetime.now ()
>>> print (json.dumps(d,cls=DateTiraeEncoder))# 使用 els 指定编码器的名称
"2024-03-14 20:49:56"

最后需要提醒的是,Python中标准模块jsem的性能比 pickle 与 cPickle 稍逊。 如果对序列化性能要求非常高的场景,可以选择 cPickle 模块。

下图显示的是这三者序列化时随着数 据规模增加所消耗时间改变的图例。

_images/45-2.png