flask的jsonify与标准模块的json.dumps有什么区别

在最初接触flask时,我也曾对jsonify这个函数产生过类似的疑问,既然标准模块json的dumps方法也可以将字典转成json格式的字符串,那么为什么还要弄出一个jsonify呢?直到我阅读了flask的源码,方解开心中的疑问。

1. json.dumps

先来看标准模块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):

json.dumps在对数据进行序列化时,只对部分类型的数据进行转化,这些数据类型包括

python json
dict object
list, tuple array
str string
int, float number
True true
False false
None null

如果在dumps的过程中,遇到了其他类型的数据,就会报错,比如下面的程序

import json
import datetime

data = {
    'name': 'python',
    'time': datetime.datetime.now(),
    'test': None
}

data_str = json.dumps(data)
print(data_str)

执行后报错内容为

TypeError: Object of type 'datetime' is not JSON serializable

对象的类型是datetime,不可进行json序列化。对于这种情况,我们可以自定义一个转换方式,dumps方法有一个参数cls,默认情况下,cls将被赋值成JSONEncoder 这个类来负责对数据进行转换,因此,我们只需要创建一个类来继承它,并重载它的default方法即可,default方法专门用来处理那些标准数据类型之外的数据(上表中的那些数据)。
修改后的程序如下

import json
import datetime

data = {
    'name': 'python',
    'time': datetime.datetime.now(),
    'test': None
}


class MyJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")

        return json.JSONEncoder.default(obj)

data_str = json.dumps(data, cls=MyJsonEncoder)
print(data_str)

2. jsonify

jsonify里面所使用的json模块来自于第三方库itsdangerous, 这个模块提供了一系列方法将可信的数据传入不可信的环境。itsdangerous中的json也不是自己开发的,而是用了simplejson,据说比标准库json更快。

事情似乎水落石出了,但这不能解答本文的问题,jsonify与json.dumps的区别,本质上他们都是对数据进行序列化,他们都遵守json标准,区别在于,flask的jsonify提供了一个JSONEncoder用来解决标准json模块dumps不能转换的数据,JSONEncoder类定义如下

class JSONEncoder(_json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return http_date(o.utctimetuple())
        if isinstance(o, date):
            return http_date(o.timetuple())
        if isinstance(o, uuid.UUID):
            return str(o)
        if dataclasses and dataclasses.is_dataclass(o):
            return dataclasses.asdict(o)
        if hasattr(o, "__html__"):
            return text_type(o.__html__())
        return _json.JSONEncoder.default(self, o)

在第一小节中,我自己实现了一个MyJsonEncoder,只对datetime数据类型做了转换处理,而flask提供的JSONEncoder则对很多种数据类型做了处理,因此更加安全,这就是他们之间的区别。

如果我们在flask应用中使用标准模块json,就必须自己提供JSONEncoder,才能应对非标准类型的数据,而使用jsonify就不需要操心这些事情了。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案