基于Hive SQL 提取加工數據是每個數據分析師的工作日常,但屏幕面前的你是否遇到過這樣的囧境:數倉表中的某個字段并非是以往那種一行一個實體信息的結構化數據,而是 json格式的半結構化數據。
如果你的SQL高級函數掌握得不夠熟練,那么面對這種存儲排列方式極為復雜的json數據必然會顯得手足無措。所以今天讓我們帶大家仔細研究一下 json 數據的結構,同時給到大家解析json的思路和模版,以快速地解析 json 數據。
一、json數據格式有哪些?
json 的數據格式,分為 json 數組 (array)和 json 對象 (object)兩種。對于 json 對象,其特征就是多個屬性是被 {} 括起來的;而 json 數組,其實就是包含了多個 json 對象的一個集合,數組是以 數組括號 [] 括起來的。以下分別舉例說明:
這里,{} 雙括號表示對象; [] 中括號表示數組; "" 雙引號內是屬性或值; : 冒號表示后者是前者的值(注意:這個值可以是字符串、數字、也可以是另一個數組或對象)
對于復雜的json數據,其屬性對應的值往往不是單純的字符串或數字,而是一個數組或對象,這給json解析增加更大的難度。
比如,下面的例子中,json_a 中 tags 屬性對應的值為一個數組; 而 json_b 中的data對應的 屬性的值為一個對象,該對象中dataInfo對應的值為字符串,但該字符串又是由一個帶雙引號的json組成。
小tips:如果想快速了解某json是否存在互相嵌套的關系,可以使用json網頁工具進行結構識別(https://www.sojson.com/simple_json.html)
json_a 的存儲格式更為常見些,因此后續會以json_a為例給大家講解解析json需要用到的函數。
二、解析json需要用到哪些函數?
由于 json_a 的排列格式更為常見些,因此后續會以json_a為例給大家講解解析json需要用到的函數。json_a 的格式如下:
json_a= {"userId": "張三", "tags": [{"tag": "男"} ,{"tag": "高收入","value":"2w+"}]}
1. get_json_object
用途:用于獲取某個key的具體值。特點在于:一次只能獲取一個key的值。
用法:函數第一個參數填寫json變量;第二個參數中,使用$表示json變量標識,然后用 . 讀取對象,用 [] 讀取數組。
舉例1:讀取json對象
舉例2:讀取json數組
2. json_tuple
用途:比json_tuple更強大,用來一次性解析json字符串中的多個字段
用法:函數第一個參數填寫json變量,后面參數填寫 key 的名稱
舉例:
值得注意的是,如果要把json_a 中的tag 按照行輸出,則以上兩個函數都顯得無能為力,需要用到以下的函數。
3. explode
用法:explode()函數的參數輸入是 array或者map 類型的數據,它可以將 array 或 map 里面的元素按照行的形式輸出。具體可以配合 LATERAL VIEW 一起使用。
為方便大家理解,這里簡單介紹下 array 格式和map 格式,順便介紹下struct 格式。
array 格式舉例:
>> ["北京","上海","天津","杭州"]
map 格式舉例:
>> {"語文":60,"數學":80,"英語":99}
struct 格式舉例:
>> {"course":"english","score":80}
{"course":"math","score":89}
{"course":"chinese","score":95}
舉例說明 explode()的用法
4. LATERAL VIEW explode和LATERAL VIEW json_tuple
LATERAL VIEW explode可以將explode展開的結果行與輸入表的列名進行表連接。同時,FROM子句可以有伴隨多個LATERAL VIEW子句,后續的LATERAL VIEWS可以引用出現在LATERAL VIEW左側的任何表格中的列。
而LATERAL VIEW json_tuple 函數一般是跟在LATERAL VIEW explode后面使用,用于拆解多列。
兩個函數用法示例:
5. regexp_replace 和 regexp_extract 以及正則匹配表達式
值得注意的是,假設我們要將json_a中的tag按列輸出,但由于explode()函數的輸入只能是map或array,如果直接將tags的json數組作為輸入,系統會報錯。
正確的方法是,將tags的json數組兩邊的中括號去掉,然后按照一定規則進行分列,以轉換為map格式。因此,需要用到下面的正則函數以及分隔split函數。
1) 正則表達式大全
參考以下網址: https://www.jb51.net/article/97732.htm
2) regexp_replace
舉例:去掉所有中擴號[]
3) regexp_extract
舉例:只去掉首末中擴號[]
6. split
用途:支持使用正則表達式對字符串進行切割,返回值為數組,因此常作為explode的輸入
用法:第一個參數為待切割的變量,第二個參數為切割符號
注意:所有正則表達式中的預定義字符比如?,},|,逗號,分號等需要在這里用\進行反轉義才能表達本意。比如正則表達式中w表示匹配字母,所以也屬于預定義字符,單獨的w表示匹配的是字母w,而\w才表示匹配字母。
三、經典實戰案例
1. 案例 A
需求背景:hive表中某字段tag按行存儲了每個用戶的多個標簽,但如果想要計算每個標簽下的用戶數,需要將tag里的userID、tag、weight字段信息擴展抽取出來。
字段tag的數據取值如下:
思路整理:
1. userID 可用 get_json_object 函數直接取出;
2. tag和weight提取
a. 先取出每個tags的子json結構。用 get_json_object 函數取出tags,然后用正則和split處理成map格式,用 LATERAL VIEW explode 函數以行展開;
b. 解析子json結構的tag和weight。用 LATERAL VIEW json_tuple 函數解析并以行展開
以下為可在 Hive 環境里執行的代碼:
小tips:上面案例的數據格式是json數據的常見格式,后續重復遇到與之高度類似的json結構概率極大,到時可以直接套用上述中的代碼思路進行快速解析,因此建議收藏以上代碼。
2. 案例 B(難度升級)
該題比案例A 難度升高,具體為:
1)json結構中object的K-V值不固定;
2)dataInfo對應的值為字符串,由一個帶雙引號的json組成。雙引號的存在導致無法正常使用get_json_object函數
原數據:
思路整理:
1)先通過正則函數處理dataInfo對應的值的雙引號,以正常的使用get_json_object函數
2)對于object的K-V值不固定的情況,可以通過冒號分割截取;
代碼:
以上便是全部內容啦。相信大家在閱讀完本文后,如果再遇到復雜的json解析問題,至少可以做到不再焦灼了,可以直接套用以上的解析模版和思路進行解析。所以,記得收藏或者點個在看哦~