如何正確地使用靜態語言處理結構未知的 json/xml/html
前言
我日常使用最多的語言是 Python,對一個 Pythonista 而言,處理 json 是一件很簡單的事:
In [1]: import json
In [2]: data_json = '{"email": "xxx@example.com", "phone": "13800000001", "nick_name": "ryan_c"}'
In [3]: data_dict = json.loads(data_json) # 返回 dict 對象
In [4]: data_dict
Out[4]: {'email': 'xxx@example.com', 'phone': '13800000001', 'nick_name': 'ryan_c'}
In [5]: json.dumps(data_dict) # 返回 json 字符串
Out[5]: '{"email": "xxx@example.com", "phone": "13800000001", "nick_name": "ryan_c"}'
我們寫爬蟲的時候經常這么干,因為實在是很方便。
寫日常小腳本時,很少見 pythonista 使用 class 定義 DTO(data type object),然后對它進行序列化(Marshal)/反序列化(UnMarshal).
然而這種方法在靜態語言中行不通,而且也是非常不推薦的用法。這就直接導致使用靜態語言處理未知的 json 數據時,會顯得非常麻煩,體驗遠遠不如 Python。
這主要是因為靜態語言需要在編譯期就確定對象的類型!
以 Java 為例,如果我們不定義 DTO,打算將一個 json 解析為一個 Map 對象,那么問題就來了:Map 的 key 和 value 的類型必須在編譯期決定。
可我們編譯時根本還不知道 json 數據長什么樣!整個 json 數據的 key/value 都是未知的,嵌套深度也是未知的,它的屬性甚至可能是動態的。。。
那這樣就話,就只能把 Map 定義成 Map<Object, Object>。這樣 json 是可以解析了,但是處理過程中你又需要做各種各樣的類型轉換。。。
而且大量使用 Object 完全拋棄掉了靜態語言的好處。
如果查看 Java 的知名 json 解析庫 jackson,能發現它提供了 ObjectMapper 和 JsonNode 樹模型進行 Json 的"流式解析",不過用起來可比 Python 麻煩多了。
總之,靜態語言需要處理 json 數據與類型的映射關系,導致對未知數據結構的解析變得很復雜。
解決方法
方法一
大部分時候,我們都可以通過定義 DTO 來解決上述問題。
方法二
而在某些情況下,比如做爬蟲/數據的清洗過濾時,我們只關心 json/html/xml 中的某些屬性,完全不 care 其他數據。
這時可以使用一些數據提取專用的語言提取數據,如 JSONPath/正則/css選擇器/xpath,所有的主流語言基本都有對應的輪子可用。
方法三
如果前兩種方法都不能滿足你的要求,那就只能選擇最后的方法:使用 Map<Object, Object>/JsonNode(Java)、map[string]interface{}(Go),然后自己去遍歷了。
參考
- Go 語言:How to Parse JSON in Golang (With Examples)
- Java 語言:jackson

浙公網安備 33010602011771號