Hive/PrestoでのJSONの展開方法

Hiveでのjsonの展開方法についてのメモ

こんなJSONを無邪気に入れてしまっていないだろうか。

[{"key":"k1","value":"v11"},{"key":"k2","value":"v12"}]

さてこのデータをこんな感じのテーブルに展開したいが、どうしたらいいのだろうか。 配列で入ってればそのままExplodeすればいいが、Stringで格納されている場合はどうしたらいいのだろうか。

key value
k1 v11
k2 v12

答えだけを知りたい人は以下を参照されたし。

WITH source AS (
SELECT 'KEY001' AS key_col, '[{"key":"k1","value":"v11"},{"key":"k2","value":"v12"}]' AS json_array_col
UNION ALL
SELECT 'KEY002' AS key_col, '[{"key":"k2","value":"v22"},{"key":"k3","value":"v23"}]' AS json_array_col
UNION ALL
SELECT 'KEY003' AS key_col, '[{"key":"k1","value":"v31"},{"key":"k3","value":"v33"}]' AS json_array_col
)

SELECT 
       get_json_object(single_json_table.single_json, '$.key') AS key,
       get_json_object(single_json_table.single_json, '$.value') AS value
FROM
(SELECT EXPLODE(split(regexp_replace(substr(json_array_col,2,LENGTH(json_array_col)-2),'\\},', '},,,,'),',,,,')) as single_json from source) single_json_table

何をやっているか

目的 : 複数のjson一行に格納されているため、1行あたり1JSONにする。 => EXPLODEを使う。

大体の場合こういったJSONはデータを格納することを第一目的にしているため、Stringになっている。 残念ながら、そのままExplodeは使えない。 ではどうするか? 配列を返す関数をSplitを挟むのだ。

案1 : そのままSplitをする。

これを採用するとjsonでもarrayでもなにかが生成される。

SELECT EXPLODE(split(json_array_col,'\},')) as single_json from source
col1
[{"key":"k1","value":"v11"
{"key":"k2","value":"v12"}]

案2 [ ]を外して文字列にする。

1. 配列の [ ] を外す

SELECT substr(json_array_col,2,LENGTH(json_array_col)-2) as single_json from source

こんな感じの結果が返ってくる。

col1
{"key":"k1","value":"v11"},{"key":"k2","value":"v12"}

2. 分割(Split)したい箇所をSplitしやすいように,を,,にする。

SELECT regexp_replace(substr(json_array_col,2,LENGTH(json_array_col)-2),'\},', '},,') as single_json from source

なぜこんなことをするか。最終的にEXPLODEしたものはJSONとして取り回したいのだ。 もし、この過程を無視してしまうと以下のようなデータが生成されてしまう。

col1
["{\"key\":\"k1\",\"value\":\"v11\"","{\"key\":\"k2\",\"value\":\"v12\"}"]

Splitの結果として当然ながら'},'は消失する。 結果としてJSONとして扱えないデータにしかならないのだ。

そのためregexp_replaceを使って、別のメルクマークを作成するのだ。

3. Splitして配列にする。

SELECT split(regexp_replace(substr(json_array_col,2,LENGTH(json_array_col)-2),'\},', '},,'),',,') as single_json from source

こんな感じの結果になる。

col1
["{\"key\":\"k1\",\"value\":\"v11\"}","{\"key\":\"k2\",\"value\":\"v12\"}"]

ようやくほしい形の配列になった。

4. Explodeする。

SELECT EXPLODE(split(regexp_replace(substr(json_array_col,2,LENGTH(json_array_col)-2),'\},', '},,'),',,')) as single_json from source

結果はこうだ。

col1
{"key":"k1","value":"v11"}
{"key":"k2","value":"v12"}

あとは好きにJSONとして加工してくれればいい。