Step Functions での JSONata を使用したデータ変換 - AWS Step Functions

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

Step Functions での JSONata を使用したデータ変換

JSONata を使用すると、強力なオープンソースのクエリおよび式言語を利用して、ワークフロー内のデータを選択して変換できます。JSONata の簡単な概要と完全なリファレンスについては、JSONata.org のドキュメントを参照してください。

次のビデオでは、Step Functions の変数と JSONata を DynamoDB の例で説明しています。

既存のワークフローで JSONata のクエリおよび変換言語を使用するには、明示的に設定する必要があります。コンソールでワークフローを作成するときは、ステートマシンのトップレベル QueryLanguage に JSONata を選択することをお勧めします。JSONPath を使用する既存のワークフローまたは新しいワークフローを対象に、コンソールでは個々のステートを JSONata に変換するオプションが用意されています。

JSONata を選択すると、ワークフローフィールドの JSONPath フィールドは 5 つ (InputPathParametersResultSelectorResultPathOutputPath) から 2 つ (ArgumentsOutput のみ) に減ります。また、JSON オブジェクトのキー名に .$ を使用することがなくなります

Step Functions を初めて使用する場合は、JSONata の式が次の構文で記述されることだけを知っておけば十分です。

JSONata 構文: "{% <JSONata expression> %}"

次のコードサンプルは、JSONPath から JSONata への変換を示しています。

# Original sample using JSONPath { "QueryLanguage": "JSONPath", // Set explicitly; could be set and inherited from top-level "Type": "Task", ... "Parameters": { "static": "Hello", "title.$": "$.title", "name.$": "$customerName", // With $customerName declared as a variable "not-evaluated": "$customerName" } }
# Sample after conversion to JSONata { "QueryLanguage": "JSONata", // Set explicitly; could be set and inherited from top-level "Type": "Task", ... "Arguments": { // JSONata states do not have Parameters "static": "Hello", "title": "{% $states.input.title %}", "name": "{% $customerName %}", // With $customerName declared as a variable "not-evaluated": "$customerName" } }

入力が { "title" : "Doctor" } で、変数 customerName"María" を割り当てた場合、どちらのステートマシンも次の JSON 結果を生成します。

{ "static": "Hello", "title": "Doctor", "name": "María", "not-evaluated": "$customerName" }

次の図では、JSONPath (左) から JSONata (右) への変換により、ステートマシン内のステップ構造が簡素化される方法を示しています。

JSONPath ステートと JSONata ステートのフィールド比較図。

(オプション) ステートの入力からのデータを選択・変換し、Arguments に入れて、統合されたアクションに渡すことができます。JSONata を使用すると、(オプションで) アクションからの結果を選択・変換して、変数への割り当てやステートの Output に使用できます。

注: AssignOutput のステップは並列で実行されます。変数の割り当て中にデータを変換した場合、その変換されたデータは Output ステップでは使用できません。Output ステップで JSONata 変換を再適用する必要があります。

JSONata クエリ言語を使用するステートの論理構造図

QueryLanguage フィールド

ワークフローの ASL 定義では、ステートマシンのトップレベルと個々のステートに QueryLanguage フィールドがあります。個々のステート内で QueryLanguage を設定することで、ステートマシン全体を一度にアップグレードするのではなく、既存のステートマシンに JSONata を段階的に導入できます。

QueryLanguage フィールドは "JSONPath" または "JSONata" に設定できます。トップレベルの QueryLanguage フィールドは省略された場合、デフォルトで "JSONPath" になります。ステートにステートレベルの QueryLanguage フィールドが含まれている場合は、そのステートに指定されたクエリ言語が使用されます。ステートに QueryLanguage フィールドが含まれていない場合は、トップレベルの QueryLanguage フィールドで指定されたクエリ言語が使用されます。

JSON 文字列での JSONata 式の記述

ASL フィールドの値、JSON オブジェクトのフィールド、または JSON 配列要素内の文字列が {% %} 文字で囲まれている場合、その文字列は JSONata として評価されます。このとき、文字列は {% で始まり (先頭に空白を含まない)、%} で終わる (末尾に空白を含まない) 必要があります。式の開始・終了が正しくないと、検証エラーになります。

例:

  • "TimeoutSeconds" : "{% $timeout %}"

  • Task ステート内の "Arguments" : {"field1" : "{% $name %}"} フィールド

  • Map ステート内の "Items": [1, "{% $two %}", 3] フィールド

すべての ASL フィールドが JSONata を受け入れるわけではありません。例えば、各ステートの Type フィールドは定数文字列に設定する必要があります。同様に、Task ステートの Resource フィールドは定数文字列である必要があります。Map 状態Itemsフィールドは、JSON 配列、JSON オブジェクト、または配列またはオブジェクトに対して評価する必要がある JSONata 式を受け入れます。

予約変数: $states

Step Functions では、$states という名前の予約変数が定義されています。JSONata のステートでは、次の構造が JSONata の式用に $states に割り当てられます。

# Reserved $states variable in JSONata states $states = { "input": // Original input to the state "result": // API or sub-workflow's result (if successful) "errorOutput": // Error Output (only available in a Catch) "context": // Context object }

ステートの開始時に、Step Functions ではステートの入力が $states.input に割り当てます。$states.input の値は、JSONata 式を受け入れるすべてのフィールドで使用できます。$states.input は常にステートの入力の元の値を参照します。

TaskParallel、および Map ステートの場合:

  • API またはサブワークフローが成功した場合、$states.result はその未加工の結果を参照します。

  • API またはサブワークフローが失敗した場合、$states.errorOutput はそのエラー出力を参照します。

    $states.errorOutputCatch フィールドの Assign または Output で使用できます。

アクセスできないステートやフィールドで $states.result$states.errorOutput にアクセスしようとすると、ステートマシンの作成、更新、検証時にキャッチされます。

$states.context オブジェクトは、StartTime、タスクトークン、初期ワークフロー入力など、特定の実行に関する情報をワークフローに提供します。詳細についてはStep Functions の Context オブジェクトから実行データにアクセスする を参照してください。

式エラーの処理

実行時に、JSONata 式の評価は次のようなさまざまな理由で失敗することがあります。

  • 型エラー{% $x + $y %} などの式は、$x または $y が数値でないと失敗します。

  • 型の非互換 – 式は、フィールドで受け入れられない型に評価されると失敗します。例えば、フィールド TimeoutSeconds は数値入力を必要とするため、式 {% $timeout %} は、$timeout が文字列を返すと失敗します。

  • 範囲外の値 – 式は、フィールドの受け入れ範囲外の値を生成すると失敗します。例えば、{% $evaluatesToNegativeNumber %} などの式は TimeoutSeconds フィールドでは失敗します。

  • 結果が返されない – JSON は未定義の値の式を表現できないため、式 {% $data.thisFieldDoesNotExist %} はエラーになります。

いずれの場合も、インタープリタはエラー States.QueryEvaluationError をスローします。Task、Map、および Parallel ステートでは、Catch フィールドでエラーをキャッチし、Retry フィールドでエラー時の再試行処理を行うことができます。

JSONPath から JSONata への変換

以降のセクションでは、JSONPath と JSONata で記述されたコードの違いを比較・説明します。

Path フィールドの廃止

ASL では、JSONPath を使用する場合、ステートデータから値を選択するために、TimeoutSecondsPath などのフィールドの Path バージョンを使用する必要がありました。JSONata を使用するとき、Path フィールドはもう使用しません。ASL が TimeoutSeconds などの非 Path フィールド内の {% %} で囲まれた JSONata 式を自動的に解釈するためです。

  • JSONPath のレガシー例: "TimeoutSecondsPath": "$timeout"

  • JSONata: "TimeoutSeconds": "{% $timeout %}"

同様に、 Map状態ItemsPathは、JSON 配列、JSON オブジェクト、または配列またはオブジェクトに評価する必要がある JSONata 式を受け入れる Itemsフィールドに置き換えられました。

JSON オブジェクト

ASL では、Parameters および ResultSelector フィールド値の JSONPath 式を含むことができる JSON オブジェクトを表現するために、ペイロードテンプレート という用語を使用していました。ASL では、JSONata に対してペイロードテンプレートという用語を使用しません。JSONata の評価は、文字列が単独で出現するか、JSON オブジェクトまたは JSON 配列内で出現するかにかかわらず、すべての文字列に対して行われるためです。

.$ の廃止

ASL では、JSONPath と組み込み関数を使用するために、ペイロードテンプレートのフィールド名に「.$」を付加する必要がありました。"QueryLanguage":"JSONata" を指定する場合、「.$」の記法はもう使用しません。代わりに、JSONata 式を {% %} 文字で囲みます。この記法は、オブジェクトや配列の階層が深くても、すべての文字列値に適用されます。

Arguments フィールドと Output フィールド

QueryLanguageJSONata に設定すると、従来の I/O 処理フィールド (InputPathParametersResultSelectorResultPathOutputPath) は無効になり、ほとんどのステートに ArgumentsOutput という 2 つの新しいフィールドが使用されるようになります。

JSONata では、これらの新しいフィールドを使用することで、JSONPath で使用されるフィールドよりも簡潔に I/O 処理を行うことができます。JSONata の機能により、ArgumentsOutput は、JSONPath での従来の 5 つのフィールドよりも柔軟に使用できます。これらのフィールド名により ASL の記述が簡素化され、値の受け渡しの処理モデルが明確になります。

Arguments および Output フィールド (および ItemSelector ステートの Map などの類似フィールド) では、次のような JSON オブジェクトを受け入れます。

"Arguments": { "field1": 42, "field2": "{% jsonata expression %}" }

または、JSONata 式を直接使用することもできます。例:

"Output": "{% jsonata expression %}"

Output は、あらゆる型の JSON 値を受け入れることもできます。例: "Output":true"Output":42

Arguments および Output フィールドは JSONata 専用であるため、JSONPath を使用するワークフローで使用することはできません。逆に、InputPathParametersResultSelectorResultPathOutputPath などの JSONPath フィールドは JSONPath 専用であるため、JSONata をトップレベルのワークフローまたはステートのクエリ言語として用いている場合は使用できません。

Pass ステート

Pass ステートでのオプションの Result は、以前は仮想タスクの Output として扱われていました。ワークフローまたはステートのクエリ言語として JSONata を選択すると、新しい Output フィールドを使用できるようになりました。

Choice ステート

JSONPath を使用する場合、Choice ステートには入力 Variable と、次の NumericLessThanEqualsPath ような多数の比較パスがあります。

# JSONPath choice state sample, with Variable and comparison path "Check Price": { "Type": "Choice", "Default": "Pause", "Choices": [ { "Variable": "$.current_price.current_price", "NumericLessThanEqualsPath": "$.desired_price", "Next": "Send Notification" } ], }

JSONata では、Choice ステートには Condition があり、そこで JSONata 式を使用できます。

# Choice state after JSONata conversion "Check Price": { "Type": "Choice", "Default": "Pause" "Choices": [ { "Condition": "{% $current_price <= $states.input.desired_priced %}", "Next": "Send Notification" } ]

注: 変数と比較フィールドは、JSONPath でのみ使用できます。Condition は JSONata でのみ使用できます。

JSONata の例

次の例は Workflow Studio で作成し、JSONata を試すために使用できます。ステートマシンを作成して実行することも、Test ステートを使用してデータを渡したり、ステートマシン定義を編集したりすることもできます。

例: Input と Output

この例では、JSONata を明示的に有効にするときに、$states.input を使用してステートの入力を取得し、Output フィールドを使用してステートの出力を指定する方法を示しています。

{ "Comment": "Input and Output example using JSONata", "QueryLanguage": "JSONata", "StartAt": "Basic Input and Output", "States": { "Basic Input and Output": { "QueryLanguage": "JSONata", "Type": "Succeed", "Output": { "lastName": "{% 'Last=>' & $states.input.customer.lastName %}", "orderValue": "{% $states.input.order.total %}" } } } }

ワークフローを次の入力で実行する場合:

{ "customer": { "firstName": "Martha", "lastName": "Rivera" }, "order": { "items": 7, "total": 27.91 } }

Test ステートまたはステートマシンの実行結果として、次の JSON 出力が返されます。

{ "lastName": "Last=>Rivera", "orderValue": 27.91 }
テスト対象ステートの入力と出力を示すスクリーンショット

例: JSONata を使用したフィルタリング

JSONata Path 演算子を使用してデータをフィルタリングできます。例えば、入力として商品のリストがあり、カロリーがゼロの商品のみを処理するとします。次の ASL でステートマシン定義を作成し、続くサンプル入力で FilterDietProducts ステートをテストできます。

JSONata を使用したフィルタリングのためのステートマシン定義

{ "Comment": "Filter products using JSONata", "QueryLanguage": "JSONata", "StartAt": "FilterDietProducts", "States": { "FilterDietProducts": { "Type": "Pass", "Output": { "dietProducts": "{% $states.input.products[calories=0] %}" }, "End": true } } }

テスト用のサンプル入力

{ "products": [ { "calories": 140, "flavour": "Cola", "name": "Product-1" }, { "calories": 0, "flavour": "Cola", "name": "Product-2" }, { "calories": 160, "flavour": "Orange", "name": "Product-3" }, { "calories": 100, "flavour": "Orange", "name": "Product-4" }, { "calories": 0, "flavour": "Lime", "name": "Product-5" } ] }

ステートマシンのステップをテストした結果の出力

{ "dietProducts": [ { "calories": 0, "flavour": "Cola", "name": "Product-2" }, { "calories": 0, "flavour": "Lime", "name": "Product-5" } ] }
テスト対象の JSONata 式の出力例。

Step Functions が提供する JSONata 関数

JSONata には、文字列、数値、集計、Boolean、配列、オブジェクト、日時、高階関数のための関数ライブラリが含まれています。Step Functions には、JSONata 式で使用できる追加の JSONata 関数が用意されています。これらの組み込み関数は、Step Functions の組み込み関数の代わりに使用できます。組み込み関数は、JSONPath クエリ言語を使用するステートでのみ使用できます。

注: パラメータとして整数値を必要とする組み込み JSONata 関数は、与えられた整数以外の数値を自動的に切り捨てます。

$partitionStates.ArrayPartition 組み込み関数に相当する JSONata の関数で、大きな配列を分割するために使用します。

最初のパラメータは分割する配列、2 番目のパラメータはチャンクサイズを表す整数です。戻り値は 2 次元配列になります。インタープリタは入力配列をチャンクサイズで指定されたサイズの複数の配列にチャンクします。配列に残っている項目の数がチャンクサイズよりも小さい場合、最後の配列チャンクの長さは前の配列チャンクの長さよりも短くなることがあります。

"Assign": { "arrayPartition": "{% $partition([1,2,3,4], $states.input.chunkSize) %}" }

$rangeStates.ArrayRange 組み込み関数に相当する JSONata の関数で、値の配列を生成するために使用します。

この関数は 3 つの引数を取ります。最初の引数は新しい配列の最初の要素を表す整数、2 番目の引数は新しい配列の最後の要素を表す整数、3 番目の引数は新しい配列の要素のデルタ値 (整数) です。戻り値は、関数の最初の引数から 2 番目の引数までの範囲で、デルタ値に従って並べられた要素からなる新しい配列です。デルタ値には正数または負数を指定でき、前の値にそのデルタを加えたり引いたりしながら、終了値に達するか超えるまで値を生成します。

"Assign": { "arrayRange": "{% $range(0, 10, 2) %}" }

$hashStates.Hash 組み込み関数に相当する JSONata の関数で、指定された入力のハッシュ値を計算するために使用します。

この関数は 2 つの引数を取ります。最初の引数はハッシュ対象のソース文字列です。2 番目の引数はハッシュ計算に使用するハッシュアルゴリズムを表す文字列です。ハッシュアルゴリズムには、"MD5""SHA-1""SHA-256""SHA-384""SHA-512" のいずれかの値を指定する必要があります。戻り値は、データの計算されたハッシュの文字列です。

JSONata がネイティブでハッシュの計算機能をサポートしていないため、この関数が追加されました。

"Assign": { "myHash": "{% $hash($states.input.content, $hashAlgorithmName) %}" }

$randomStates.MathRandom 組み込み関数に相当する JSONata の関数で、ランダムな数値 n (0 ≤ n < 1) を返します。

この関数は、乱数関数のシード値を表すオプションの整数引数を取ります。この関数を同じシード値で使用すると、同じ数が返されます。

組み込みの JSONata 関数 $random がシード値を受け入れないため、このオーバーロードされた関数が追加されました。

"Assign": { "randNoSeed": "{% $random() %}", "randSeeded": "{% $random($states.input.seed) %}" }

$uuidStates.UUID 組み込み関数に相当する JSONata の関数。

この関数は引数を取りません。この関数は v4 UUID を返します。

JSONata がネイティブで UUID を生成する機能をサポートしていないため、この関数が追加されました。

"Assign": { "uniqueId": "{% $uuid() %}" }

$parse – JSON 文字列を逆シリアル化する JSONata 関数。

この関数は、JSON 形式の文字列を唯一の引数として取ります。

JSONata は $eval を通じてこの機能をサポートしていますが、$eval は Step Functions のワークフローではサポートされていません。

"Assign": { "deserializedPayload": "{% $parse($states.input.json_string) %}" }