解決方法
[] を付けることで、明示的に Array を返すことができます。
{
"QueryLanguage": "JSONata",
"Type": "Task",
...
"Assign": {
// $map の結果を常に配列で返したい場合は末尾に`[]`を付ける
"expected-array": "{% $map(...)[] %}",
// ネストしている場合、`[]` はパスのどこにおいてもOK
"phone-numbers": "{% $phone[][type='home'].number %}",
"phone-numbers": "{% $phone[type='home'][].number %}",
"phone-numbers": "{% $phone[type='home'].number[] %}"
}
}
この記事を書いた理由
業務案件で Step Functions を使い始めましたが、とっても便利です。特に JSONata が去年から使えるようになったお陰で、自由度が高く大変助かってます。
ただ JSONata で実装していく中で、気になった挙動がありました。$Map や $distinct した結果の配列の長さが 1 だった場合、Array 型ではなく Object 型と評価されてしまうのです。この挙動により Step Functions の Map ステート等は Array 型を期待していたため、エラーになってしまいました。
JSONata の挙動調査
上記の挙動は JSONata 上の仕様らしく、$Mapの説明に下記のような記述がありました。
If the input argument is an array with 1 element, returns the single result of applying the function parameter to each value in the array parameter.
$map([2], function($v) { $v * 2 })evaluates to4
上記の Google 日本語訳が下記です。
入力引数が1要素の配列の場合、配列引数の各値に関数引数を適用した結果を1つ返します。
$map([2], function($v) { $v * 2 })は4と評価されます。
より詳しく調べると、JSONata は単一の値を参照された場合は、その値のみを返すのが理にかなっているためアンラップする仕様があるとの記述がありました。ただしこの仕様ですとプログラム言語によっては不都合が生じるため、常に配列を期待する場合は [] をロケーションパス内に記述すればよいそうです。
JSONata をブラウザ上で実行できるサイトを公式が用意しているので、気になる方は試してみてください。
感想
最初は $Append で空配列と結合して、無理やり配列を返してました。公式ドキュメントは読んだほうが良いですね。
参考文献