Hive中如何處理JSON格式數據


Hive 處理json數據總體來說有三個辦法:

  • 使用內建的函數get_json_object、json_tuple

  • 使用自定義的UDF(一進一出),自定義UDTF(一進多出)

  • 第三方的SerDe--》JSONSerder

 

1、使用內建函數處理

get_json_object(string json_string, string path)

返回值:String

說明:解析json字符串json_string,返回path指定的內容;如果輸入的json字符串無效,那么返回NUll;函數每次只能返回一個數據項;

 

json_tuple(jsonStr, k1, k2, ...)

返回值:所有的輸入參數、輸出參數都是String;

說明:參數為一組鍵k1,k2,。。。。。和json字符串,返回值的元組。該方法比get_json_object高效,因此可以在一次調用中輸入多個鍵;

 

explode,使用explod將Hive一行中復雜的 array 或 map 結構拆分成多行。

 

測試數據:

user1;18;male;{"id": 1,"ids": [101,102,103],"total_number": 3}
user2;20;female;{"id": 2,"ids": [201,202,203,204],"total_number": 4}
user3;23;male;{"id": 3,"ids": [301,302,303,304,305],"total_number": 5}
user4;17;male;{"id": 4,"ids": [401,402,403,304],"total_number": 5}
user5;35;female;{"id": 5,"ids": [501,502,503],"total_number": 3}

 

建表加載數據:

CREATE TABLE IF NOT EXISTS jsont1(
username string,
age int,
sex string,
json string
)
row format delimited fields terminated by ';';

load data local inpath '/root/data/weibo.json' overwrite into table jsont1;

 

json的處理:

-- get 單層值
select username, age, sex, get_json_object(json, "$.id") id,
get_json_object(json, "$.ids") ids,
get_json_object(json, "$.total_number") num
from jsont1;

-- get 數組
select username, age, sex, get_json_object(json, "$.id") id,
get_json_object(json, "$.ids[0]") ids0,
get_json_object(json, "$.ids[1]") ids1,
get_json_object(json, "$.ids[2]") ids2,
get_json_object(json, "$.ids[3]") ids3,
get_json_object(json, "$.total_number") num
from jsont1;

-- 使用 json_tuple 一次處理多個字段
select json_tuple(json, 'id', 'ids', 'total_number')
 from jsont1;

-- 有語法錯誤
select username, age, sex, json_tuple(json, 'id', 'ids', 'total_number')
 from jsont1;

-- 使用 explode + lateral view
-- 在上一步的基礎上,再將數據展開
-- 第一步,將 [101,102,103] 中的 [ ] 替換掉
-- select "[101,102,103]"
-- select "101,102,103"
select regexp_replace("[101,102,103]", "\\[|\\]", "");

-- 第二步,將上一步的字符串變為數組
select split(regexp_replace("[101,102,103]", "\\[|\\]", ""), ",");

-- 第三步,使用explode + lateral view 將數據展開
select username, age, sex, id, ids, num
 from jsont1
lateral view json_tuple(json, 'id', 'ids', 'total_number') t1 as id, ids, num;

with tmp as(
select username, age, sex, id, ids, num
 from jsont1
lateral view json_tuple(json, 'id', 'ids', 'total_number') t1 as id, ids, num
)
select username, age, sex, id, ids1, num
 from tmp
lateral view explode(split(regexp_replace(ids, "\\[|\\]", ""), ",")) t1 as ids1;

小結:json_tuple 優點是一次可以解析多個json字段,對嵌套結果的解析操作復雜;

 

2、使用UDF處理

自定義UDF處理json串中的數組。自定義UDF函數:

  • 輸入:json串、數組的key

  • 輸出:字符串數組

     

pom文件

<properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>

   <dependencies>
      <dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.3.7</version>
</dependency>
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.1.23</version>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>2.3.2</version>
               <configuration>
                   <source>1.8</source>
                   <target>1.8</target>
               </configuration>
           </plugin>
           <plugin>
               <artifactId>maven-assembly-plugin</artifactId>
               <configuration>
                   <descriptorRefs>
                       <descriptorRef>jar-with-dependencies</descriptorRef>
                   </descriptorRefs>
               </configuration>
               <executions>
                   <execution>
                       <id>make-assembly</id>
                       <phase>package</phase>
                       <goals>
                           <goal>single</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>

 

package cn.lagou.dw.hive.udf;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.junit.Test;

import java.util.ArrayList;

public class ParseJsonArray extends UDF {
   public ArrayList<String> evaluate(String jsonStr, String arrKey){
       if (Strings.isNullOrEmpty(jsonStr)) {
           return null;
      }

       try{
           JSONObject object = JSON.parseObject(jsonStr);
           JSONArray jsonArray = object.getJSONArray(arrKey);
           ArrayList<String> result = new ArrayList<>();
           for (Object o: jsonArray){
               result.add(o.toString());
          }

           return result;
      } catch (JSONException e){
           return null;
      }
  }

   @Test
   public void JunitParseJsonArray(){
       String str = "{\"id\": 1,\"ids\": [101,102,103],\"total_number\": 3}";
       String key = "ids";
       ArrayList<String> evaluate = evaluate(str, key);
       System.out.println(JSON.toJSONString(evaluate));
  }
}

 

使用自定義 UDF 函數:

-- 添加開發的jar包(在Hive命令行中)
add jar /root/edu_jars/my_udf.jar;

-- 創建臨時函數。指定類名一定要完整的路徑,即包名加類名
create temporary function lagou_json_array as "com.lagou.edu.ParseJsonArray";

-- 執行查詢
-- 解析json串中的數組
select username, age, sex, lagou_json_array(json, "ids") ids
 from jsont1;

-- 解析json串中的數組,並展開
select username, age, sex, ids1
 from jsont1
lateral view explode(lagou_json_array(json, "ids")) t1 as ids1;

-- 解析json串中的id、num
select username, age, sex, id, num
 from jsont1
lateral view json_tuple(json, 'id', 'total_number') t1 as id, num;

-- 解析json串中的數組,並展開
select username, age, sex, ids1, id, num
 from jsont1
lateral view explode(lagou_json_array(json, "ids")) t1 as ids1  
lateral view json_tuple(json, 'id', 'total_number') t1 as id, num;

3、 使用UDTF處理

UDTF返回結果如下:

id ids total_number
1 101 3
1 102 3
1 103 3

 

package com.lagou.edu.udtf;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
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.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

import java.util.List;

/**
* 官網編寫UDTF地址:https://cwiki.apache.org/confluence/display/Hive/DeveloperGuide+UDTF
*/
public class ParseJsonArrayUDTF extends GenericUDTF {
   private String[] obj = new String[3];
   /**
    * 輸出字段名稱及數據類型
    * @param argOIs
    * @return
    * @throws UDFArgumentException
    */

   @Override
   public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
       //輸出的字段名稱列表
       List<String> colName = Lists.newLinkedList();
       colName.add("id");
       colName.add("ids");
       colName.add("total_number");
       //輸出的結果數據類型
       List<ObjectInspector> resType = Lists.newLinkedList();
       resType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
       resType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
       resType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
       //返回列名與字段值的數據類型
       return ObjectInspectorFactory.getStandardStructObjectInspector(colName, resType);
  }

   @Override
   public void process(Object[] args) throws HiveException {
       //第一個字段
       if (args[0] == null ) {
           return;
      }
       String jsonStr = args[0].toString();
       JSONObject object = JSON.parseObject(jsonStr);
       //獲取到id
       String id = object.getString("id");
       String total_number = object.getString("total_number");
       JSONArray ids = object.getJSONArray("ids");
       obj[0]=id;
       obj[2]=total_number;
       for (Object o : ids) {
           obj[1]=o.toString();
           System.out.println(obj[0]+"_"+obj[1]+"_"+obj[2]);
           forward(obj);
      }

  }

   @Override
   public void close() throws HiveException {

  }


   public static void main(String[] args) throws HiveException {
       ParseJsonArrayUDTF p = new ParseJsonArrayUDTF();
       String str = "{\"id\": 1,\"ids\": [101,102,103],\"total_number\": 3}";
       p.process(new String[]{str});
  }
}

hive> add jar /root/jars/myudtf.jar;
 
hive> create temporary function myudtf as 'com.lagou.edu.udtf.ParseJsonArrayUDTF';
 
select username, age, sex, t1.id,t1.ids,t1.total_number
from jsont1
lateral view myudtf(json) t1 as id,ids,total_number;

 

4、使用SerDe處理

序列化是對象轉換為字節序列的過程;反序列化是字節序列恢復為對象的過程;

對象的序列化主要有兩種用途:

  • 對象的持久化,即把對象轉換成字節序列后保存到文件中

  • 對象數據的網絡傳送

 

SerDe 是Serializer 和 Deserializer 的簡寫形式。Hive使用Serde進行行對象的序列與反序列化。最后實現把文件內容映射到 hive 表中的字段數據類型。SerDe包括 Serialize/Deserilize 兩個功能:

  • Serialize把Hive使用的java object轉換成能寫入HDFS字節序列,或者其他系統能識別的流文件

  • Deserilize把字符串或者二進制流轉換成Hive能識別的java object對象

Read : HDFS files => InputFileFormat => <key, value> => Deserializer => Row object

Write : Row object => Seriallizer => <key, value> => OutputFileFormat => HDFS files

常見:https://cwiki.apache.org/confluence/display/Hive/DeveloperGuide#DeveloperGuide-HiveSerDe

 

Hive本身自帶了幾個內置的SerDe,還有其他一些第三方的SerDe可供選擇。

create table t11(id string)
stored as parquet;

create table t12(id string)
stored as ORC;

desc formatted t11;
desc formatted t12;

LazySimpleSerDe(默認的SerDe)

ParquetHiveSerDe

OrcSerde

 

對於純 json 格式的數據,可以使用 JsonSerDe 來處理。

{"id": 1,"ids": [101,102,103],"total_number": 3}
{"id": 2,"ids": [201,202,203,204],"total_number": 4}
{"id": 3,"ids": [301,302,303,304,305],"total_number": 5}
{"id": 4,"ids": [401,402,403,304],"total_number": 5}
{"id": 5,"ids": [501,502,503],"total_number": 3}

 

create table jsont2(
id int,
ids array<string>,
total_number int
)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe';

load data local inpath '/root/data/json2.dat' into table jsont2;
select id, ids[0] as ids1,ids[1] as ids2,ids[2] as ids3  ,total_number from jsont2;

 

各種Json格式處理方法小結:

1、簡單格式的json數據,使用get_json_object、json_tuple處理

2、對於嵌套數據類型,可以使用UDF,UDTF

3、純json串可使用JsonSerDe處理更簡單

 

 

 

 

獲取更多資料

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM