はじめに
JSONata を使って JSON の階層化されたオブジェクトをフラット化する方法についていろいろ模索してみたためその結果をまとめる。
きっかけは、AWS Step Functions でステート間のデータ変換で JSONata が使えるようになり 1、早速使ってみたところJSONをフラット化したい場面が出てきて本記事のトピックについて考えることになったことだ。同じ問題に直面した人の参考になれば幸いである。
環境
以下のバージョンで動作確認済み。
- JSONata 2.0.6
動作確認は JSONata Exerciser を利用。
クエリ対象のJSON
今回扱うJSONのサンプル。
{
"target": {
"a": 0,
"b": {
"b1": 1,
"b2": 2,
"b3": 3
},
"c": {
"c1": 4,
"c2": 5
},
"d": 6,
"e": 7
}
}
クエリ結果として求めるもの
上記のJSONを以下のように変換したいものとする。
{
"a": 0,
"b1": 1,
"b2": 2,
"b3": 3,
"c1": 4,
"c2": 5,
"d": 6,
"e": 7
}
クエリ
その1: 泥臭いやり方
.
演算子を使って欲しい項目を取って所望にキーに割り当てるだけ。
項目が少ないならいいが、多かったり動的に項目数が変わったりする場合は厳しい。
{
"a": target.a,
"b1": target.b.b1,
"b2": target.b.b2,
"b3": target.b.b3,
"c1": target.c.c1,
"c2": target.c.c2,
"d": target.d,
"e": target.e
}
その2: 組込関数を使って少し楽をするやり方
組込関数 $merge(array<object>)
を利用して、 target.b
, target.c
の中身を一々書かなくて済む。
元々フラットな階層にあった target.a
などはオブジェクトとして用意してあげる必要があるのが難点。
親階層の項目が少なく、フラット化したい子階層の項目が多い場合にはこれでも十分と思われる。
$merge([
{"a": target.a},
target.b,
target.c,
{"d": target.d, "e": target.e}
])
その3: よりシンプルに組込関数を活用するやり方
その2で $merge([...])
の中に入れた各要素を $map
で動的に作るやり方。
親階層にも項目が多い場合にも耐えられる。
$merge([
$map($keys(target), function($k) {
$k in ["b", "c"] ? $lookup(target, $k) : {$k: $lookup(target, $k)}
})
])
クエリ分解・解説
1. $keys(target)
-
target
オブジェクトのキー(["a", "b", "c", "d", "e"]
)を配列として取得
2. $map(array, function)
-
$map
は配列の各要素にfunction
2 を適用し、その結果の配列を返すもの - ここでは
$keys(target)
で得た各キー$k
に対して処理を行う
3. function($k) { ... }
- 中身は三項演算子 (
条件式 ? 真の場合の値 : 偽の場合の値
) を使っている -
$k
が "b" または "c" の場合は、target.b
やtarget.c
の中身(オブジェクト)をそのまま返す - それ以外のキーは、
{$k: $lookup(target, $k)}
で「キー: 値」の形のオブジェクトにして返す
4. $merge([...])
-
$map
で作った配列を$merge
で1つのオブジェクトにまとめる
ここで登場した $merge
, $keys
, $lookup
について詳しくは公式ドキュメント 3 を参照されたい。