1.看源碼
打開site-package,flask,json,__init__.py
jsonify回去調用default()函數,我們最關心的就是重寫default方法
我們是不是調用jsonify就一定會調用default呢?答案:不是!
發現如上圖所示,並沒有進入jsonify的default方法裏,而是直接把字典給序列化出來了。
那麼什麼時候會調用default呢?
結論:如果flask知道如何序列化你傳入進來的數據結構的時候,是不會調用default,因爲知道如何序列化就直接幫我們序列化了,但是如果我們要序列化一個對象,是我們的user模型,flask默認是不知道怎麼去序列化這個模型的,那麼就會去調用default函數,爲什麼會這樣的,原因就在於flask不知道怎麼序列化,但是它會給我們一個途徑,讓我們來指明這個數據結構應該怎麼序列化,換句話說,default函數最主要的就是我們需要在內部把不能序列化的結構轉化爲可以序列化的結構,比如我們傳入進來的是一個user,user是不能序列化的,但是如果我們可以把user轉化成字典,字典是可以序列化的,那麼這樣就能完成user對象的序列化了,雖然user作爲一個模型他不能序列化,但是我們可以把他的信息讀取出來,轉化爲一個字典,從而保證我們整個序列化的成功執行。可以看到default裏面的源碼,傳入的user對象既不是datetime也不是date、uuid.UUID、__html__,所以最後會拋出一個異常
所以我們要在default中把不能序列化的user轉化成可以序列化的格式。
所以我們繼承,然後重寫default方法,在重寫的函數中實現user的可序列化就OK了
2、重寫默認的default函數,實現自己的序列化機制
我們不要直接修改源碼,要在外部繼承JSONEncoder,然後在用自己的方法覆蓋原來的default方法。
-
from flask import Flask, jsonify
-
class hehe:
-
name = 'zhangsan'
-
age = 18
-
-
app = Flask(__name__)
-
ctx = app.app_context()
-
ctx.push()
-
# 上面是解決上下文對象的異常RuntimeError: Working outside of application context.
-
-
a = hehe()
-
print(a)
-
jsonify(a) # TypeError: Object of type 'hehe' is not JSON serializable
可以看到上圖代碼報錯不能序列化a對象,所以我們要在外部繼承JSONEncoder,然後在用自己的方法覆蓋原來的default方法。
可以看到即使按照上圖所示寫,flask還是沒有調用我們自己定義的default,所以我們還要在flask裏面替換一下
-
from flask import Flask as _Flask, jsonify
-
from flask.json import JSONEncoder as _JSONEncoder
-
-
-
class JSONEncoder(_JSONEncoder):
-
-
def default(self, o):
-
pass
-
-
-
class Flask(_Flask):
-
json_encoder = JSONEncoder
-
-
-
class hehe():
-
name = 'zhangsan'
-
age = 18
-
-
-
app = Flask(__name__)
-
ctx = app.app_context()
-
ctx.push()
-
# 上面是解決上下文對象的異常RuntimeError: Working outside of application context.
-
-
a = hehe()
-
print(a)
-
-
jsonify(a) # 不報錯了
可以看到flask把我們實例化的hehe類當做參數o傳遞了進來,有兩個屬性
3、把對象轉化成字典
3.1 __dict__的方式
現在我們要把對象轉化成字典,因爲字典是可以被序列化的, 但是對象不行。我們想到了對象的__dict__內置方法,但是發現沒有得到任何的結果,輸出的是一個空的json對象。
這是因爲我們在hehe類裏面定義的是類的變量而不是實例的變量。類的變量是不會被存放到對象的__dict__當中的。所以加入一個實例變量的時候就有值了(如下圖)
所以我們看到這種方式是可以的,但是我們想把無論類變量還是實例變量都像把它直接序列化,我們就需要用下面的方法把所有的都轉成字典
3.2、定義keys和__getitem__的方式
python 對象轉字典及序列化對象相關問題,__dict__!!!!必看,多坑
-
class D:
-
name = 'zhangsan'
-
age = 18
-
-
def __init__(self):
-
self.sex = '男'
-
-
def keys(self):
-
return ('name', 'sex')
-
-
def __getitem__(self, item):
-
return getattr(self, item)
-
-
-
d = D()
-
print(d.__dict__) # {'sex': '男'}
-
print(dict(d)) # {'name': 'zhangsan', 'sex': '男'} 如果註銷了getitem方法就會報錯TypeError: 'D' object is not iterable
4、最終的代碼實現
-
from flask import Flask as _Flask, jsonify
-
from flask.json import JSONEncoder as _JSONEncoder
-
-
-
class JSONEncoder(_JSONEncoder):
-
def default(self, o):
-
if hasattr(o, 'keys') and hasattr(o, '__getitem__'):
-
print(dict(o))
-
else:
-
print("不能序列化對象")
-
-
-
class Flask(_Flask):
-
json_encoder = JSONEncoder
-
-
-
class hehe:
-
name = 'zhangsan'
-
age = 18
-
-
def __init__(self):
-
self.sex = '男'
-
-
def keys(self):
-
return ('name', 'sex')
-
-
def __getitem__(self, item):
-
return getattr(self, item)
-
-
-
app = Flask(__name__)
-
ctx = app.app_context()
-
ctx.push()
-
# 上面是解決上下文對象的異常RuntimeError: Working outside of application context.
-
a = hehe()
-
print(a) # <__main__.hehe object at 0x7f0aed3e1e10>
-
jsonify(a) # {'name': 'zhangsan', 'sex': '男'}
5、關於default函數的其他知識
default函數是被遞歸調用的,之所以我們沒看到被遞歸調用是因爲我們定義的類都太簡單了,如果對象下面的某個屬性是另外一個對象的情況
只要遇到不能序列化的對象,都會傳入default裏面讓我們來解決,
例如下面所示default調用了兩次: