Json 格式的資料處理
Json 資料格式是我們比較常用的的一種資料格式,例如埋點資料、業務端的資料、前後端呼叫都採用的是這種資料格式,所以我們很有必要學習一下這種資料格式的處理方法
準備資料
cat json.data
{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}
{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}
{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}
{"movie":"3408","rate":"4","timeStamp":"978300275","uid":"1"}
{"movie":"2355","rate":"5","timeStamp":"978824291","uid":"1"}
{"movie":"1197","rate":"3","timeStamp":"978302268","uid":"1"}
{"movie":"1287","rate":"5","timeStamp":"978302039","uid":"1"}
{"movie":"2804","rate":"5","timeStamp":"978300719","uid":"1"}
{"movie":"594","rate":"4","timeStamp":"978302268","uid":"1"}
建立hive表並且載入資料
create table ods.ods_json_data(text string);
load data local inpath "/Users/XXX/workspace/hive/json.data" overwrite into table ods.ods_json_data;
get_json_object 和 json_tuple 函式
json_tuple 不支援json 的巢狀處理,但是支援一次性獲取多個頂級的key對應的值
get_json_object 不支援一次獲取多個值,但是支援複雜json 的處理
get_json_object()
用法:get_json_object(string json_string, string path) 前面我們介紹過如何檢視函式的用法desc function get_json_object
返回值:String
說明:解析json的字串json_string,返回path指定的內容。如果輸入的json字串無效,那麼返回NUll,這個函式每次只能返回一個資料項。
具體示例: get_json_object(value,’$.id’)
select get_json_object(text,"$.movie") from ods.ods_json_data;
這個函式的不足之處是,它只能返回一個值,就是我們不能一次性從json 中提取多個值,如果要提取多個值的話,就要多次呼叫這個函式,但是我們下面介紹的json_tuple 就可以,但是這不是說這個函式不強或者怎麼樣,記住這個函式的api 可以幫你節約很多時間
json_tuple
用法:json_tuple(jsonStr, p1, p2, ..., pn) 整理的pn 就是我們要提取的鍵
返回值:tuple(v1,...vn) 這裡的返回值v1 ... vn 和 鍵p1 .... pn 是相對應的
select json_tuple(text,'movie','rate','timeStamp','uid') from ods.ods_json_data;
json_tuple相當於get_json_object的優勢就是一次可以解析多個Json欄位。
例子演示
1. 巢狀json 的處理
前面我們說了json_tuple不支援巢狀JSON 的處理
select get_json_object('{"movie":"594","rate":"4","timeStamp":"978302268","uid":"1","info":{"name":"天之驕子"}}',"$.info.name")
select json_tuple('{"movie":"594","rate":"4","timeStamp":"978302268","uid":"1","info":{"name":"天之驕子"}}',"info.name")
2. Json陣列解析(get_json_object 實現)
SELECT get_json_object('[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]', '$.[0].website'), get_json_object('[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]', '$.[1].website');
這個時候時候你發現我提取的都是json 陣列中的website
,有沒有什麼簡單的辦法呢,理論上get_json_object 只能有一個返回值,無論如何都需要寫多個,那你有沒有想過一個問題,我要是這個陣列裡面有100個元素都是json,我需要每一個json 的website 那我是不是需要寫100次了,這個時候你要是仔細閱讀這個函式的api 的話,你就會發現了另外一個符號*
SELECT get_json_object('[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]', '$.[*].website')
這下你知道了,get_json_object 是隻能返回一個元素,不是隻能返回一個字串,上面本來就是一個json 陣列,那要是我們是從json 裡面解析出來的陣列怎麼處理呢?
SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info');
需要注意下面這樣操作之後你拿到的就是一個json 字串了,這下你就可以按照上面的方式再處理一次了
select get_json_object (get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info' ),'$.[1].website');
但是有時候我們希望直接獲取,而不是通過這樣巢狀的方式,這個時候其實就是將上面的巢狀的get_json_object函式的path 引數進行組合
SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info[1].website');
這個時候如果我們再上 * 進行加持,那就很簡單了
SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info[*].website');
其實到這裡我們學習了指定一個陣列的某個下標獲取一個元素,指定* 獲取全部元素,那就如我就想獲取前三個或者偶數個或者奇數個呢,哈哈,如果你回過頭去看api 你就是知道了提供了一個Union operator
,指定任意你想組合的下標即可,獲取
SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info[0,1].website');
下面我們嘗試獲取一下偶數個,或者奇數個或者是一定範圍內的奇數個或者偶數個,其實就是上面提供的陣列切片,你可以參考api 進行使用
SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"},{"website":"www.ikeguang2.com","name":"我的生活記憶"}]}', '$.info[0:2:2].website');
但是我嘗試了一下,發現這個功能有bug,不能做到切片的效果,每次都是全部返回
SELECT get_json_object('{"info":[
{"website":"www.ikeguang.com","name":"我的生活記憶"},
{"website":"beian.ikeguang.com","name":"備案"},
{"website":"www.ikeguang2.com","name":"我的生活記憶"}]}', '$.info[0:2:2].website');
載入JSON 資料
對於上面json.data 的資料,我們能不能在load 資料到hive 的時候就處理,而不是load 完之後再到使用的時候去處理,尤其是針對這種巢狀結構不是很複雜的這種json 格式
create table ods.ods_json_parse_data(
movie string,
rate string,
`timeStamp` string,
uid string)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS TEXTFILE;
load data local inpath "/Users/liuwenqiang/workspace/hive/json.data" overwrite into table ods.ods_json_parse_data;
這種方法需要注意的是你的資料型別和欄位名稱都要匹配,否則就會報錯或者不能獲取到值,那要是複雜一點的巢狀結構呢,其實也可以,在上面的資料基礎上新增了一個巢狀的欄位也是可以的
{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1","info":{"name":"天之驕子"}}
create table ods.ods_json_parse_data2(
movie string,
rate string,
`timeStamp` string,
uid string,
info map<string,string>)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS TEXTFILE;
load data local inpath "/Users/liuwenqiang/workspace/hive/json.data" overwrite into table ods.ods_json_parse_data2;
總結
- get_json_object 和 json_tuple 函式的使用場景和其優缺點
- 如果json 格式比較簡單,那麼可以在建表載入資料的時候就可以將json 處理掉,如果比較複雜也可以再載入的時候解析一部分,然後再通過SQL 進行解析
- 也可以嘗試寫一些UDF 函式來處理JSON
關注公眾號:大資料技術派,回覆"資料",領取
1024G
資料。