在 Step Functi JSONata ons 中使用转换数据 - Amazon Step Functions
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

在 Step Functi JSONata ons 中使用转换数据

借 JSONata助,您可以获得强大的开源查询和表达式语言,用于在工作流程中选择转换数据。有关简要介绍和完整 JSONata 参考资料,请参阅 JSONata.org 文档

您必须选择使用现有工作流程的 JSONata 查询和转换语言。在控制台中创建工作流程时,我们建议选择 JSONata 顶级状态机QueryLanguage。对于使用的现有工作流程或新工作流程 JSONPath,控制台提供了将单个状态转换为的选项 JSONata。

选择后 JSONata,您的工作流程字段将从五个 JSONPath 字段(InputPathParametersResultSelectorResultPath、和OutputPath)减少到只有两个字段: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" } }

给定customerName分配给的输入{ "title" : "Doctor" }和变量"María",两台状态机都将生成以下 JSON 结果:

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

在下图中,你可以看到一个图形表示形式,显示了转换 JSONPath (左)到 JSONata (右)将如何降低状态机中步骤的复杂性:

比较 JSONPath 和 JSONata 状态中的字段的示意图。

您可以(可选)从状态输入中选择数据并将其转换为参数,然后发送到您的集成操作。然后 JSONata,您可以(可选)选择并转换操作的结果,以分配给变量和状态输出

注意:赋值输出步骤并行进行。如果您选择在变量赋值期间转换数据,则转换后的数据将无法在 “输出” 步骤中使用。必须在 “输出” 步骤中重新应用 JSONata 转换。

使用 JSONata 查询语言的状态的逻辑图。

QueryLanguage 字段

在您的工作流程 ASL 定义中,有一个位于状态机定义的顶层和各个状态的QueryLanguage字段。通过QueryLanguage在单个状态内进行设置,可以在现有状态机 JSONata 中逐步采用,而不是一次性升级所有状态机。

QueryLanguage字段可以设置为"JSONPath""JSONata"。如果省略顶级QueryLanguage字段,则默认为"JSONPath"。如果某个状态包含州级QueryLanguage字段,则 Step Functions 将使用该状态的指定查询语言。如果该州不包含QueryLanguage字段,则它将使用顶级QueryLanguage字段中指定的查询语言。

用 JSON 字符串编写 JSONata 表达式

当 ASL 字段、JSON 对象字段或 JSON 数组元素的值中的字符串被{% %}字符包围时,该字符串的计算方式将为 JSONata 。注意,字符串必须以没有前导空格开头,并且必须以%}没有尾随空格结尾。{%不当打开或关闭表达式将导致验证错误。

一些示例:

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

  • "Arguments" : {"field1" : "{% $name %}"}处于一种Task状态

  • "Items": [1, "{% $two %}", 3]处于一种Map状态

并非所有 ASL 字段都接受 JSONata。例如,必须将每个州的Type字段设置为常量字符串。同样,Task状态的Resource字段必须是常量字符串。Map状态Items字段将接受 JSON 数组或必须计算为数组的 JSONata 表达式。

保留变量:$states

Step Functions 定义了一个名为的保留变量$states。在 JSONata 状态中,分配了以下结构以$states供在 JSONata 表达式中使用:

# 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状态:

  • $states.result如果成功,则指 API 或子工作流程的原始结果。

  • $states.errorOutput指的是 API 或子工作流程失败时的错误输出。

    $states.errorOutput可以在Catch现场的Assign或中使用Output

$states.errorOutput在创建、更新$states.result或验证状态机时,如果尝试访问或处于无法访问的字段和状态,则会被捕获。

$states.context对象为您的工作流程提供有关其特定执行的信息StartTime,例如任务令牌和初始工作流程输入。要了解更多信息,请参阅 在 Step Functions 中从 Context 对象访问执行数据

处理表达式错误

在运行时, JSONata 表达式求值可能由于多种原因而失败,例如:

  • 类型错误-如果$x或不是数字{% $x + $y %}$y则表达式(例如)将失败。

  • 类型不兼容-表达式的计算结果可能为该字段不接受的类型。例如,该字段TimeoutSeconds需要数字输入,因此如果$timeout返回字符串,则表达式{% $timeout %}将失败。

  • 值超出范围-生成超出字段可接受范围的值的表达式将失败。例如,诸如之类的表达式{% $evaluatesToNegativeNumber %}将在TimeoutSeconds字段中失败。

  • 无法返回结果-JSON 不能表示未定义的值表达式,因此该表达式{% $data.thisFieldDoesNotExist %}会导致错误。

在每种情况下,解释器都会抛出错误:States.QueryEvaluationError. 您的任务、地图和并行状态可以提供一个Catch用于捕捉错误的Retry字段和一个用于重试错误的字段。

从 JSONPath 转换为 JSONata

以下各节比较并解释了使用 JSONPath 和编写的代码之间的区别 JSONata。

没有更多的路径字段

ASL 要求开发人员在使用时使用 JSONPath字段Path版本从状态数据中选择值,如中所TimeoutSecondsPath示。使用时 JSONata,您将不再使用Path字段,因为 ASL 会自动为您解释非路径字段中{% %}包含的 JSONata 表达式,例如。TimeoutSeconds

  • JSONPath 旧版示例:"TimeoutSecondsPath": "$timeout"

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

同样,Map状态ItemsPath已替换为接受 JSON 数组或必须计算为数组的 JSONata 表达式的Items字段。

JSON 对象

ASL 使用术语有效载荷模板来描述可以包含ParametersResultSelector字段值 JSONPath 表达式的 JSON 对象。ASL 不会使用术语有效负载模板, JSONata 因为所有字符串都会 JSONata 进行评估,无论它们是单独出现的,还是出现在 JSON 对象或 JSON 数组中。

别再说了。 $

ASL 要求您在要使用的有效载荷模板 JSONPath 和内部函数中的字段名称后附加 .$ “”。当您指定时"QueryLanguage":"JSONata",您不再对 JSON 对象字段名称使用 .$ “” 约定。而是用{% %}字符将 JSONata 表达式括起来。无论对象嵌套在其他数组或对象中的深度如何,您都对所有字符串值字段使用相同的约定。

参数和输出字段

当设置QueryLanguage为时JSONata,旧的 I/O 处理字段将被禁用(InputPathParametersResultSelectorResultPathOutputPath),并且大多数状态将获得两个新字段:ArgumentsOutput

JSONata 与使用的字段相比,提供了一种更简单的执行 I/O 转换的方法。 JSONPath JSONata的功能比前五个字段Output更强大 JSONPath。Arguments这些新的字段名称还有助于简化您的 ASL,并阐明传递和返回值的模型。

ArgumentsOutput字段(以及其他类似的字段,例如Map状态字段ItemSelector)将接受 JSON 对象,例如:

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

或者,你可以直接使用 JSONata 表达式,例如:

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

输出也可以接受任何类型的 JSON 值,例如:"Output":true"Output":42

ArgumentsOutput字段仅支持 JSONata,因此在使用的工作流程中使用它们是无效的 JSONPath。相反,InputPathParametersResultSelectorResultPathOutputPath、和其他 JSONPath 字段仅在中受支持 JSONPath,因此在 JSONata 用作顶级工作流程或状态查询语言时,使用基于路径的字段是无效的。

通过状态

可选的 “通过” 状态的结果以前被视为虚拟任务的输出。 JSONata 选择为工作流或状态查询语言后,您现在可以使用新的 “输出” 字段。

选择状态

使用时 JSONPath,选择状态具有输入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,选择状态有一个你可以使用 JSONata 表达式Condition的地方:

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

注意:变量和比较字段仅适用于 JSONPath。状况仅适用于 JSONata。

JSONata 例子

可以在 Workflow Studio 中创建以下示例进行实验 JSONata。您可以创建和执行状态机,也可以使用测试状态来传入数据,甚至修改状态机的定义。

示例:输入和输出

此示例说明在您选择加入时$states.input如何使用状态输入和Output字段来指定状态输出 JSONata。

{ "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 } }

测试状态或状态机执行将返回以下 JSON 输出:

{ "lastName": "Last=>Rivera", "orderValue": 27.91 }
屏幕截图显示了被测状态的输入和输出。

示例:使用筛选 JSONata

您可以使用 JSONata 路径运算符筛选数据。例如,假设你有一份可供输入的产品清单,而你只想处理卡路里含量为零的产品。您可以使用以下 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 表达式的输出示例。

JSONata Step Functions 提供的函数

JSONata 包含字符串、数字、聚合、布尔函数、数组、对象、日期/时间和高阶函数的函数库。Step Functions 提供了可以在 JSONata 表达式中使用的其他 JSONata 函数。这些内置函数可用作 Step Functions 内部函数的替代品。内部函数仅在使用 JSONPath 查询语言的状态下可用。

注意:需要整数值作为参数的内置 JSONata 函数会自动向下舍入所提供的任何非整数。

$partiti JSONata on- 等同于对大型数组进行分区的States.ArrayPartition内在函数。

第一个参数是要分区的数组,第二个参数是代表区块大小的整数。返回值将是一个二维数组。解释器将输入数组分成多个数组,其大小由区块大小指定。如果数组中剩余的项目数小于区块大小,则最后一个数组区块的长度可能小于之前的数组区块的长度。

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

$rang e- JSONata 相当于生成值数组的States.ArrayRange内在函数。

这个函数需要三个参数。第一个参数是代表新数组第一个元素的整数,第二个参数是代表新数组最后一个元素的整数,第三个参数是新数组中元素的增量值整数。返回值是一个新生成的值数组,其范围从函数的第一个参数到函数的第二个参数,中间的元素由 delta 进行调整。增量值可以是正值或负值,它将从最后一个元素开始递增或递减每个元素,直到达到或超过最终值。

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

$hash- JSONata 等效于计算给定States.Hash输入哈希值的内在函数。

这个函数需要两个参数。第一个参数是要进行哈希处理的源字符串。第二个参数是一个字符串,表示用于哈希计算的哈希算法。哈希算法必须是以下值之一:"MD5"、、"SHA-1""SHA-256""SHA-384""SHA-512"。返回值是计算出的数据哈希值的字符串。

之所以创建此函数,是因为 JSONata 它本身不支持计算哈希的功能。

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

$random- JSONata 等同于返回随机数 n 的States.MathRandom内在函数,其中。0 ≤ n < 1

该函数采用一个可选的整数参数,表示随机函数的种子值。如果您使用具有相同种子值的函数,它将返回相同的数字。

之所以创建这个重载函数,是因为内置 JSONata 函数$random不接受种子值。

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

$uuid-States.UUID 内部函数的 JSONata 版本。

该函数不带任何参数。此函数返回一个 v4 的 UUID。

之所以创建此函数,是因为 JSONata 它本身不支持生成 UUIDs功能。

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

$parse-用于反序列化 JSON 字符串的 JSONata 函数。

该函数将字符串化的 JSON 作为其唯一参数。

JSONata 通过支持此功能$eval;但是,Step Fun $eval ctions 工作流程不支持此功能。

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