python的json.dumps自定义序列化操作

Python继承JSONEncoder类,重写default,实现自定义序列化操作

json支持python的类型转化对象类型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Supports the following objects and types by default:

+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+

可以看到,大部分的类型都可以通过json.dumps进行序列化,但是有些特殊的对象,例如事件对象,decimal.Decimal,以及uuid.UUID类型等。此时我们就需要手动重写default方法,来实现对这些类型的序列化。


举个我的django项目中的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from datetime import datetime,date
import json
from decimal import Decimal
from uuid import UUID

class JsonCustomEncoder(json.JSONEncoder):
"""对时间序列等特殊序列进行编码序列化"""
def default(self, obj):
if isinstance(obj,datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj,date):
return obj.strftime('%Y-%m-%d')
elif isinstance(obj,(Decimal,UUID,)):
return str(obj)
else:
return super().default(obj) # 抛出父类的异常

接下来调用一下:

1
2
3
4
5
6
dict_ = {
'name':'syz',
'age':21,
'hobby':'zjw',
}
m = json.dumps(dict_,cls=JsonCustomEncoder) # 通过cls参数传入类

注:JSONEncoder应该会对传入的dict_对象判断是否是可迭代对象,如果是,那么就进行迭代,依次判断里面元素是否满足json格式,如果不是,是一个对象的话,就会通过这个对象obj,手动来对obj里的属性进行json转化。

例如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import json
class Person:
def __init__(name,age):
self.name = name
self.age = age

def transform(obj):
return {
'name':obj.name,
'age':obj.age,
}

p = Person()

j = json.dumps(p,default=transform) # 函数通过default指定,p为自定义的对象,没有实现`__iter__`


最后阅读了一点源码,稍微分享一下阅读心得:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 以下是json.dumps中的一段代码:

def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw):
# cached encoder
if (not skipkeys and ensure_ascii and
check_circular and allow_nan and
cls is None and indent is None and separators is None and
default is None and not sort_keys and not kw):
return _default_encoder.encode(obj)
if cls is None:
cls = JSONEncoder
return cls(
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators, default=default, sort_keys=sort_keys,
**kw).encode(obj)

我们看这个cls参数,如果cls为None,就为cls打上默认的JSONEncoder猴子补丁,如果不为空,就说明有子类继承了JSONEncoder,传过来了参数,此时就调用JSONEncoder子类。

总结:其实不管是框架还是开源包,这样做的做法到处可见,最大的好处就是拓展性强,有效的实现解耦合。