Hive處理Json資料

大資料技術派發表於2021-11-30

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;

image-20201230163733256

這個函式的不足之處是,它只能返回一個值,就是我們不能一次性從json 中提取多個值,如果要提取多個值的話,就要多次呼叫這個函式,但是我們下面介紹的json_tuple 就可以,但是這不是說這個函式不強或者怎麼樣,記住這個函式的api 可以幫你節約很多時間

image-20201230183648635

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;

image-20201230163927109

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")

image-20201230175753453

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');

image-20201230180054218

這個時候時候你發現我提取的都是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')

image-20201230183128589

這下你知道了,get_json_object 是隻能返回一個元素,不是隻能返回一個字串,上面本來就是一個json 陣列,那要是我們是從json 裡面解析出來的陣列怎麼處理呢?

SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info');

image-20201230181300897

需要注意下面這樣操作之後你拿到的就是一個json 字串了,這下你就可以按照上面的方式再處理一次了

select get_json_object (get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info' ),'$.[1].website');

image-20201230182103977

但是有時候我們希望直接獲取,而不是通過這樣巢狀的方式,這個時候其實就是將上面的巢狀的get_json_object函式的path 引數進行組合

SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info[1].website');

image-20201230182139617

這個時候如果我們再上 * 進行加持,那就很簡單了

SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info[*].website');

image-20201230183443597

其實到這裡我們學習了指定一個陣列的某個下標獲取一個元素,指定* 獲取全部元素,那就如我就想獲取前三個或者偶數個或者奇數個呢,哈哈,如果你回過頭去看api 你就是知道了提供了一個Union operator,指定任意你想組合的下標即可,獲取

SELECT get_json_object('{"info":[{"website":"www.ikeguang.com","name":"我的生活記憶"},{"website":"beian.ikeguang.com","name":"備案"}]}', '$.info[0,1].website');

image-20201230184522813

下面我們嘗試獲取一下偶數個,或者奇數個或者是一定範圍內的奇數個或者偶數個,其實就是上面提供的陣列切片,你可以參考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;

image-20201230195730821

這種方法需要注意的是你的資料型別和欄位名稱都要匹配,否則就會報錯或者不能獲取到值,那要是複雜一點的巢狀結構呢,其實也可以,在上面的資料基礎上新增了一個巢狀的欄位也是可以的

{"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;

image-20201230201351438

總結

  1. get_json_object 和 json_tuple 函式的使用場景和其優缺點
  2. 如果json 格式比較簡單,那麼可以在建表載入資料的時候就可以將json 處理掉,如果比較複雜也可以再載入的時候解析一部分,然後再通過SQL 進行解析
  3. 也可以嘗試寫一些UDF 函式來處理JSON

關注公眾號:大資料技術派,回覆"資料",領取1024G資料。

相關文章