问题场景:
解决统一响应文本封装及json响应文本类型错误问题
❤举个栗子
def get_user():
uid = g.user.uid
user = User.query.filter_by(id=uid).first_or_404()
return jsonify(user)
报错:
TypeError: Object of type 'User' is not JSON serializable // Werkzeug Debugger
如上user返回值是一个对象,而jsonify无法将对象序列化,那怎么解决呢?
将user对象转换为字典,我们可以能会想到直接 user.__dict__
class Solution(object):
name = 'wyq'
age = 89
def __init__(self):
self.gender = '女'
o = Solution()
print(o.__dict__)
结果如下不是我们想要的,python中类变量是不会存放到__dict__
,只有实例变量才会存入。
{'gender': '女'}
那么,如果获取到__dict__
下的所有实例变量和类变量,我们就可以用jsonify序列化对象
❤解决方案
class Solution(object):
name = 'wyq'
age = 89
def __init__(self):
self.gender = '女'
def keys(self):
return ['name', 'age', 'gender']
def __getitem__(self, item):
return getattr(self, item)
o = Solution()
print(dict(o))
运行结果
{'name': 'wyq', 'age': 89, 'gender': '女'}
解决Flask中的json类型错误
Flask json转换类如下,只需要我们重新写default函数,定义转换规则,便能到达我们想要的效果。
class JSONEncoder(_json.JSONEncoder):
"""The default Flask JSON encoder. This one extends the default simplejson
encoder by also supporting ``datetime`` objects, ``UUID`` as well as
``Markup`` objects which are serialized as RFC 822 datetime strings (same
as the HTTP date format). In order to support more data types override the
:meth:`default` method.
"""
def default(self, o):
"""Implement this method in a subclass such that it returns a
serializable object for ``o``, or calls the base implementation (to
raise a :exc:`TypeError`).
For example, to support arbitrary iterators, you could implement
default like this::
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
return JSONEncoder.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 hasattr(o, '__html__'):
return text_type(o.__html__())
return _json.JSONEncoder.default(self, o)
在user对象中(也就是user的model),定义keys
和__getitem__
方法,key方法个性化可以放到对象中,getitem固定不变建议放入Base基类中
class User(Base):
id = Column(Integer, primary_key=True)
email = Column(String(24), unique=True, nullable=False)
nickname = Column(String(24), unique=True)
auth = Column(SmallInteger, default=1)
_password = Column('password', String(100))
def keys(self):
return ['id', 'email', 'nickname', 'auth']
def __getitem__(self, item):
return getattr(self, item)
自定义Flask json解析类(app.py)
# -*- coding:utf-8 -*-
from flask import Flask as _Flask
from flask.json import JSONEncoder as _JSONEncoder
from app.libs.error_code import ServerError
class JSONEncoder(_JSONEncoder):
def default(self, o):
if hasattr(o, 'keys') and hasattr(o, '__getitem__'):
return dict(o)
raise ServerError()
使用自定义Flask json解析类
from flask import Flask
from app import JSONEncoder
app = Flask(__name__)
# 返回json格式转换
app.json_encoder = JSONEncoder
if __name__ == "__main__":
app.run()
测试
最后,如果返回时间类型,或其他类型,需要在default,添加你想转换的类型
# -*- coding:utf-8 -*-
import datetime
import decimal
import uuid
from flask import Flask as _Flask
from flask.json import JSONEncoder as _JSONEncoder
from app.libs.error_code import ServerError
class JSONEncoder(_JSONEncoder):
def default(self, o):
if hasattr(o, 'keys') and hasattr(o, '__getitem__'):
return dict(o)
if isinstance(o, datetime.datetime):
# 格式化时间
return o.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(o, datetime.date):
# 格式化日期
return o.strftime('%Y-%m-%d')
if isinstance(o, decimal.Decimal):
# 格式化高精度数字
return str(o)
if isinstance(o, uuid.UUID):
# 格式化uuid
return str(o)
if isinstance(o, bytes):
# 格式化字节数据
return o.decode("utf-8")
raise ServerError()