在我们使用hive进行解析JSON时,虽然有get_json_object函数进行解析,但是功能有限,无法对一个JSON数组进行解析。那么就需要我们自定义一个函数来将数组炸裂开,分成一个个JSON对象去解析。
自定义一个类继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF
重写 initialize,process方法
需要注意的是输出forward必须是一个集合和数组,否则可能会出错。
代码:
package com.royal.hive.udtf;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.List;
/**
* @author csw
* @time 2022/4/21 19:48
*/
public class ExplodeJSONArray extends GenericUDTF {
@Override
public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
// 进行判断传参是否合法
if (argOIs.length != 1) {
throw new UDFArgumentException("传参不合法,只需要一个参数");
}
// 判断传参的类型
// 由于前面控制了传参个数,所以此时是可以确信的直接取第一个参数判断类型,getCategory返回数据类型检查器
// ObjectInspector.Category.PRIMITIVE基本数据类型
if (argOIs[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {
throw new RuntimeException("参数类型必须为基本数据类型");
}
// 将参数对象检查器强转为基本类型对象检查器
PrimitiveObjectInspector argumentOI = (PrimitiveObjectInspector) argOIs[0];
// 再去判断是否为String类型
if (argumentOI.getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING) {
throw new RuntimeException("参数必须为String类型数据");
}
// 以上均是对使用该函数时的参数和条件判断
// 定义返回值名称和类型
List<String> fieldNames = new ArrayList<>();
List<ObjectInspector> filedOIs = new ArrayList<>();
fieldNames.add("items");
filedOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
// 通过对象工厂进行创建一个Struct对象
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, filedOIs);
}
@Override
public void process(Object[] objects) throws HiveException {
// 获取值,为什么这里使用数值第一个,因为在初始化时传参就已经限制了条件,这里必只有一个值
String jsonArray = objects[0].toString();
// 解析为JSONArray数组
JSONArray actions = new JSONArray(jsonArray);
// 如何处理?遍历JSONArray数组,来获取每一个JSON对象
for (int i = 0; i < actions.length(); i++) {
// 这里由于我们hive自身有解析JSON的函数,(udtf是炸裂开的,因为这里时循环,所以实际最终输出的是一个数组)
String[] result = new String[1];
result[0] = actions.getString(i);
forward(result);
}
}
@Override
public void close() throws HiveException {
}
}
自带函数解析JSON数组:
SELECT
explode(
split(
regexp_replace(
regexp_replace(
'[{"website":"www.baidu.com","name":"百度"},{"website":"google.com","name":"谷歌"}]',
'\\[|\\]','' --将字符串的[]总括号去除(替换为‘’)
),
'\\}\\,\\{', -- 正则表达式匹配替换,主要目的时将‘,’替换为‘;‘来实现切分
'\\}\\;\\{'
),
'\\;'-- 切分
) -- 炸裂开,不然只是一个Array数组
);