Python - 製作 JSON serializable 的類別

Posted on  Mar 29, 2018  in  Python 程式設計 - 中階  by  Amo Chen  ‐ 2 min read

Python 的 json 模組十分方便,可以把 dict() tuple() list() str() int() 等資料型別轉成 JSON 字串,不過遇到像是 set() 時,就會產生以下錯誤:

>>> import json
>>> json.dumps(set())
...(略)...
TypeError: Object of type 'set' is not JSON serializable

原因在於 json.dumps() 中預設並沒有處理 set() 等型別的序列化( serialization )。

雖然如此, json.dumps() 還是有參數能夠處理這些無法被序列化的類別(class)或型別。

json.dumps() 有個參數 cls 可以代入 1 個可以幫忙轉換資料為 JSON serializable 的 class ,該類別只需繼承 json.JSONEncoder 並實作 default 方法,在該方法中實作轉換資料的功能。

p.s. 如果是使用 Django 的話,可以用 DjangoJSONEncoder 取代 json.JSONEncoder

以下為可以轉換 set 為 JSON serializable 的 list 的範例類別:

import json

class AdvancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)
        return json.JSONEncoder.default(self, obj)


print(json.dumps(set(['foo', 'bar']), cls=AdvancedJSONEncoder))

執行結果:

["foo", "bar"]

可以看到 TypeError 已經消失,因為 set() 是 JSON Serializable 了。

基於這個方法,我們可以更進一步在每個自訂的類別實作一些特殊的方法,讓自訂的類別成為 JSON serializable 的類別。

例如以下自訂的類別 User 在 json.dumps() 會出現錯誤:

import json

class User(object):
    def __init__(self, username):
        self.username = username


user = User('foo')
json.dumps(user)

執行時產生的錯誤:

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

為了讓 User 類別成為 JSON serializable ,可以改寫成為:

import json


class User(object):
    def __init__(self, username):
        self.username = username

    def __jsonencode__(self):
        return {'username': self.username}


class AdvancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, '__jsonencode__'):
            return obj.__jsonencode__()

        if isinstance(obj, set):
            return list(obj)
        return json.JSONEncoder.default(self, obj)

user = User('foo')
print(json.dumps(user, cls=AdvancedJSONEncoder))

執行結果:

{"username": "foo"}

上述範例可以看到我們改寫 AdvancedJSONEncoder 類別,並在裡面加以下的程式,判斷只要 obj 含有 __jsonencode__ 屬性,就直接呼叫並回傳它。

if hasattr(obj, '__jsonencode__'):
    return obj.__jsonencode__()

因此只要在任意類別中實作 __jsonencode__ 方法,並在裡面回傳 JSON serializable 的資料即可讓一個自訂類別成為 JSON serializable 的類別,這也就是為什麼我們在 User.__jsonencode__ 選擇回傳 dict() 型別的資料,因為它是 JSON serializable 的資料型別。

以上就是關於製作 JSON serializable 的類別的介紹。

Happy coding!

References

https://docs.python.org/3/library/json.html#json.dump

https://docs.djangoproject.com/en/2.0/topics/serialization/

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!