最近接到需求:客戶端上報json數據,入數據庫後需要對json中的key分組統計時長和次數,但json中的key是不確定的,經常會增刪。
明細層模型設計:將key和value分別設計爲1列,這樣能不需要關心json中的key和value怎麼變,都就根據客戶端上報的數據放入數據數據庫
由此引發思索:
- 在json不確定的key和value情況下,怎麼把key和value取出,並進行行轉列放入表中?
- hive自帶解析json的的函數get_json_object和json_tuple都必須要指定key才能將json解析出,並且一個key一列,value作爲key列中的值
最開始的想法是通過自定義函數實現該功能,但自定義函數有時並不是很穩定
Java實現解析json中key和value
package com.fuyun.java;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import java.util.Iterator;
/**
* @Author: 浮雲
* @Date: 2020/1/8 18:09
*/
public class ParseJsonTest {
public static void main(String[] args) {
String jsonData = "{\"name\":\"Bob\",\"age\":\"18\"}";
try {
JSONObject jsonObject = new JSONObject(jsonData);
//通過迭代器獲取這段json當中所有的key值
Iterator keys = jsonObject.keys();
//然後通過一個循環取出所有的key值
while (keys.hasNext()){
String key = String.valueOf(keys.next());
//最後就可以通過剛剛得到的key值去解析後面的json了
String value = jsonObject.optString(key);
System.out.println("key:" + key + " value:" + value);
}
} catch (JSONException je) {
je.printStackTrace();
}
}
}
以下用hive自帶函數實現該功能
表中的數據大概爲這樣:
operation_name network_type detail_load_time load_time
電信 WIFI NULL 231
電信 NONETWORK {"MonkeySdkTime":173,"shellTime":100,"shellTime2":36,"ProbeSDKTime":88,"playerSDKTime":674,"pushSDKTime":34} 398
- 將json中所有key和value解析出來轉換爲數組
select operation_name
,network_type
,arr1_detail_load_time
,load_time
from temp.temp_page_detail_load_time
--去掉兩表的{}再按,分割成數組,再行轉列
lateral view explode(split(regexp_replace(detail_load_time, '\\{|\\}', ''), ',')) arr1 as arr1_detail_load_time
limit 2;
result:
移動 WIFI "shellTime":167 3211
移動 WIFI "shellTime2":30 3211
這樣存在一個小小的bug:會過濾detail_load_time爲null的數據,優化代碼:
select operation_name
,network_type
,arr1_detail_load_time
from temp.temp_page_detail_load_time
--先將null值做轉換,再去掉兩表的{}再按,分割成數組,再行轉列
lateral view explode(split(regexp_replace(nvl(detail_load_time, '-998'), '\\{|\\}', ''), ',')) arr1 as arr1_detail_load_time
limit 2;
result:
移動 WIFI "ProbeSDKTime":198 3221
移動 WIFI "playerSDKTime":36 3221
- 將上面準換好的數組再進行分割,分別解析出key和value置爲兩列
select operation_name
,network_type
--先將"去掉,再按:分割成數組
,split(regexp_replace(arr1.arr1_detail_load_time, '"', ''), ':')[0] detail_load_time_type
,cast(nvl(split(regexp_replace(arr1.arr1_detail_load_time, '"', ''), ':')[1], 0) as bigint) detail_load_time
from temp.temp_page_detail_load_time
--去掉兩表的{}再按,分割成數組,再行轉列
lateral view explode(split(regexp_replace(nvl(detail_load_time, '-998'), '\\{|\\}', ''), ',')) arr1 as arr1_detail_load_time
limit 2;
reslut:
電信 WIFI -998 0
移動 WIFI playerSDKTime 167
這樣就大功告成!