Hive sql 解析Json數組


 

我們都知道,Hive 內部提供了大量的內置函數用於處理各種類型的需求,參見官方文檔:Hive Operators and User-Defined Functions (UDFs)。我們從這些內置的 UDF 可以看到兩個用於解析 Json 的函數:get_json_object 和 json_tuple。用過這兩個函數的同學肯定知道,其職能解析最普通的 Json 字符串,如下:

hive ( default )>  SELECT get_json_object( '{"website":"www.iteblog.com","name":"過往記憶"}' , '$.website' );
OK
www.iteblog.com
 
hive ( default )> SELECT json_tuple( '{"website":"www.iteblog.com","name":"過往記憶"}' , 'website' , 'name' );
OK
www.iteblog.com 過往記憶
Time taken: 0.074 seconds, Fetched: 1 row(s)

json_tuple 相對於 get_json_object 的優勢就是一次可以解析多個 Json 字段。但是如果我們有個 Json 數組,這兩個函數都無法處理,get_json_object 處理 Json 數組的功能很有限,如下:

hive ( default )>
                 >
                 > SELECT get_json_object( '[{"website":"www.iteblog.com","name":"過往記憶"}, {"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}]' , '$.[0].website' );
OK
www.iteblog.com
Time taken: 0.069 seconds, Fetched: 1 row(s)

如果我們想將整個 Json 數組里面的 website 字段都解析出來,如果這么寫將非常麻煩,因為我們無法確定數組的長度,而且即使確定了,這么寫可維護性也很差,所以我們需要想別的辦法。

如何在 Apache Hive 中解析 Json 數組
如果想及時了解Spark、Hadoop或者Hbase相關的文章,歡迎關注微信公共帳號: iteblog_hadoop

使用 Hive 自帶的函數解析 Json 數組

在介紹如何處理之前,我們先來了解下 Hive 內置的 explode 函數,官方的解釋是:explode() takes in an array (or a map) as an input and outputs the elements of the array (map) as separate rows. UDTFs can be used in the SELECT expression list and as a part of LATERAL VIEW. 意思就是 explode() 接收一個 array 或 map 類型的數據作為輸入,然后將 array 或 map 里面的元素按照每行的形式輸出。其可以配合 LATERAL VIEW 一起使用。光看文字描述很不直觀,咱們來看看幾個例子吧。

hive ( default )> select explode(array( 'A' , 'B' , 'C' ));
OK
A
B
C
Time taken: 4.188 seconds, Fetched: 3 row(s)
 
hive ( default )> select explode(map( 'A' ,10, 'B' ,20, 'C' ,30));
OK
A 10
B 20
C 30

相信不需要我描述大家就能看明白這個函數的意義。大家可能會問,這個函數和我們解析 Json 數組有毛關系啊。其實有關系,我們其實可以使用這個函數將 Json 數組里面的元素按照一行一行的形式輸出。根據這些已有的知識,我們可以寫出以下的 SQL 語句:

hive ( default )> SELECT explode(split(regexp_replace(regexp_replace( '[{"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}]' , '{' , '\\}\\;\\{' ), '\\[|\\]' , '' ), '\\;' ));
OK
{ "website" : "www.iteblog.com" , "name" : "過往記憶" }
{ "website" : "carbondata.iteblog.com" , "name" : "carbondata 中文文檔" }

現在我們已經能正確的解析 Json 數據了。

你現在肯定不知道上面一堆的 SQL 是啥含義,這里我來一步一步的解釋。

 

  • explode 函數只能接收數組或 map 類型的數據,而 split 函數生成的結果就是數組;
  • 第一個 regexp_replace 的作用是將 Json 數組元素之間的逗號換成分號,所以使用完這個函數之后,[{"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}] 會變成 [{"website":"www.iteblog.com","name":"過往記憶"};{"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}]
  • 第二個 regexp_replace 的作用是將 Json 數組兩邊的中括號去掉,所以使用完這個函數之后,[{"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}] 會變成 {"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}

然后我們可以結合 get_json_object 或 json_tuple 來解析里面的字段了:

hive ( default )> select json_tuple(json, 'website' , 'name' ) from ( SELECT explode(split(regexp_replace(regexp_replace( '[{"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondateblog.com","name":"carbondata 中文文檔"}]' , '\\}\\,\\{' , '\\}\\;\\{' ), '\\[|\\]' , '' ), '\\;' )) as json) iteblog;
OK
www.iteblog.com 過往記憶
carbondata.iteblog.com  carbondata 中文文檔
Time taken: 0.189 seconds, Fetched: 2 row(s)

自定義函數解析 Json 數組

雖然可以使用 Hive 自帶的函數類解析 Json 數組,但是使用起來還是有些麻煩。值得高興的是, Hive 提供了強大的自定義函數(UDF)的接口,我們可以使用這個功能來編寫解析 Json 數組的 UDF。具體的代碼如下:

package com.iteblog.udf.json;
 
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.json.JSONArray;
import org.json.JSONException;
 
import java.util.ArrayList;
 
 
@Description (name = "json_array" ,
         value = "_FUNC_(array_string) - Convert a string of a JSON-encoded array to a Hive array of strings." )
public class UDFJsonAsArray extends UDF {
     public ArrayList<String> evaluate(String jsonString) {
         if (jsonString == null ) {
             return null ;
         }
         try {
             JSONArray extractObject = new JSONArray(jsonString);
             ArrayList<String> result = new ArrayList<String>();
             for ( int ii = 0 ; ii < extractObject.length(); ++ii) {
                 result.add(extractObject.get(ii).toString());
             }
             return result;
         } catch (JSONException e) {
             return null ;
         } catch (NumberFormatException e) {
             return null ;
         }
     }
}

上面的代碼邏輯很簡單,我就不介紹了。將上面的代碼進行編譯打包,假設打包完的 jar 包名稱為 iteblog.jar,然后我們就可以如下使用這個函數了。

hive ( default )> add jar /home/iteblog/iteblog.jar;
Added [/home/iteblog/iteblog.jar] to class path
Added resources: [/home/iteblog/iteblog.jar]
hive ( default )> create temporary function json_array as 'com.iteblog.udf.json.UDFJsonAsArray' ;
OK
Time taken: 0.013 seconds
hive ( default )>
               > select explode(json_array( '[{"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondata.iteblog.com","name":"carbondata 中文文檔"}]' ));
OK
{ "website" : "www.iteblog.com" , "name" : "過往記憶" }
{ "website" : "carbondata.iteblog.com" , "name" : "carbondata 中文文檔" }
Time taken: 0.08 seconds, Fetched: 2 row(s)
 
hive ( default )> select json_tuple(json, 'website' , 'name' ) from ( SELECT explode(json_array( '[{"website":"www.iteblog.com","name":"過往記憶"},{"website":"carbondata.iteblog.com","name":"carbta 中文文檔"}]' )) as json) iteblog;
OK
www.iteblog.com 過往記憶
carbondata.iteblog.com  carbondata 中文文檔
Time taken: 0.082 seconds, Fetched: 2 row(s)

這個結果和上面使用 Hive 內置的函數結果一致。當然,你還可以實現其他的 UDF,邏輯和這個類似,就不再介紹了。


免責聲明!

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



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