Python實現JSON反序列化類對象

咱們的網絡協議通常是把數據轉換成JSON以後再傳輸。以前在Java裏面,實現序列化和反序列化,不論是jackson,仍是fastjson都很是的簡單。如今有項目須要用Python來開發,很天然的但願這樣的便利也能在Python中體現。java

可是在網上看了一些教程,講反序列化的時候,基本都是轉換爲dict或者array。這種編程方式我從情感上是沒法接受的。難道是這些JSON庫都不支持反序列化爲類對象?我立刻打消了這個念頭,Python這樣強大的腳本語言,不可能沒有完善的JSON庫。編程

因而我就研究了一下原生的json,以及第三方的demjsonsimplejsonjson

1、原生json

我仔細研究了原生jsonloads方法的定義bash

def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
複製代碼

這裏面的object_hookobject_pairs_hook參數引發了個人注意,我重點說一下object_hook網絡

官方文檔的說明以下:數據結構

object_hook is an optional function that will be called with the result of any object literal decoded (a dict). The return value of object_hook will be used instead of the dict. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting).函數

這個object_hook根據文檔的解釋就是一個自定義解碼函數,入參數標準反序列化後的dict,咱們能夠根據本身的規則轉換輸出爲想要的格式。post

我又去搜了一下object_hook,你們對於這個東西的處理方式基本就是用一個靜態方法把dict轉換成對象。測試

咱們的數據結構是這樣的ui

{"status":1,"info":"發佈成功","data":{"id":"52","feed_id":"70"}}
複製代碼

因而我就寫了這樣的代碼:

class Response:

    def __init__(self, status, info, data) -> None:
        super().__init__()
        self.status = status
        self.info = info
        self.data = data

    @staticmethod
    def object_hook(d):
        return Response(d['status'], d['info'], d['data'])
 ...
resp = json.loads(body, object_hook=Response.object_hook)
複製代碼

一開始呢,確實沒有問題,雖然用起來沒有java的json庫辣麼方便,但總歸實現了需求。

好景不長,我測試的第一個接口返回的數據中,data是字段一個字符串,反序列化正常。但是後來當接口返回的結構中data字段是一個dict結構的時候,object_hook的入參竟然變成了data字段轉換以後的dict({"id":"52","feed_id":"70"}),而不是完整的數據。

這些懵逼了,上網搜索了一圈沒有結論。好吧,我最後又老老實實回到官方文檔,read the fucking official document

不看不知道,一看嚇一跳,官方文檔用了一種巧妙的方式實現了上面的需求。

>>> class JSONObject:
...     def __init__(self, d):
...         self.__dict__ = d
...
>>>
>>> data = json.loads(s, object_hook=JSONObject)
>>> data.name
'ACME'
>>> data.shares
50
>>> data.price
490.1
>>>
複製代碼

我服了,把json解析以後的dict直接賦值給對象的屬性dict,而後就能夠爲所欲爲的使用屬性了,真心方便,動態語言就是好。

以上是官方的json庫實現方案,那另外兩個知名的第三方庫呢?

2、demjson

demjson也支持hook。有兩種配置的方式:decode函數配置和set_hook函數配置

1. decode

def decode( txt, encoding=None, **kwargs )
複製代碼

decode函數能夠指定不少參數,其中就包括hook函數。hook函數的指定是使用鍵值對的方式,鍵是hook函數的名稱,值是hook函數。

demjson是經過名字來管理hook函數的,因此hookname不是隨便指定的,必須是內置的幾種hook函數的名稱。

  • decode_number
  • decode_float
  • decode_object
  • decode_array
  • decode_string
  • encode_value
  • encode_dict
  • encode_dict_key
  • encode_sequence
  • encode_bytes
  • encode_default
demjson.decode(body, encode='utf-8',decode_obbject=Reponse.object_hook)
複製代碼

結果並無讓我很開森,依然是沒法處理嵌套結構。 日誌中顯示以下內容:

2018-01-30 16:01:17,137 poster.py post_all 73 INFO    : {"status":1,"info":"\u53d1\u5e03\u6210\u529f","data":{"id":"54","feed_id":"72"}}
2018-01-30 16:01:17,138 response.py object_hook 13 INFO    : {'id': '54', 'feed_id': '72'}
2018-01-30 16:01:17,138 response.py object_hook 13 INFO    : {'status': 1, 'info': '發佈成功', 'data': demjson.undefined}
複製代碼

很奇怪的是object_hook 函數被調用了兩次,第一次是data字段的內容,第二是所有的內容,可是data字段沒有解析出來。 很是奇怪,百思不得其解!!!

2. set_hook

set_hook函數跟上面的decode函數不同,它是JSON類的成員函數,而decode函數是個靜態函數。

def set_hook(self, hookname, function)
複製代碼

吸收以前的教訓,此次我仔細閱讀了demjson的文檔,還真發現點東西。

Netsted values. When decoding JSON that has nested objects or arrays, the decoding hooks will be called once for every corresponding value, even if nested. Generally the decoding hooks will be called from the inner-most value outward, and then left to right.

這裏重點說到嵌套的問題,出現嵌套的時候,每一個對應的類型都會調用hook函數一次,並且是從最內層,從左往右。好吧,以前出現的問題所有明白了,原來都是這個規則惹的禍,可是爲何這樣設計我暫時仍是不明白。

set_hook的使用方式

j = demjson.JSON()
    j.set_hook( 'decode_array', my_sort_array )
    j.decode(body, encode='utf-8')
複製代碼

3、simplejson

前面說了那麼多,simplejson的方式就沒什麼可說的,跟官方的jsonhook方式一致。

總結

雖然個人需求是知足了,可是仍是有一個大大的問號留在我心中,爲何是這樣設計,網上沒有找到合適的答案,剩下的須要研究源代碼分析了。

相關文章
相關標籤/搜索