

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

# 在 Step Functi JSONata ons 中使用转换数据
<a name="transforming-data"></a>

 借 JSONata助，您可以获得强大的开源查询和表达式语言，用于在工作流程**中选择**和**转换**数据。有关简要介绍和完整 JSONata 参考资料，请参阅 [JSONata.org 文档](https://docs.jsonata.org/overview.html)。

**支持的 JSONata 版本**  
Step Fun JSONata ctions 支持 2.0.6 版。

 以下视频描述了变量，并 JSONata 在 Step Functions 中以 DynamoDB 为例：




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

 选择后 JSONata，您的工作流程字段将从五个 JSONPath 字段（`InputPath`、`Parameters`、`ResultSelector``ResultPath`、和`OutputPath`）减少到只有两个字段：`Arguments`和`Output`。此外，您**不会**在 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"
  }
}
```

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

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

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

![\[比较 JSONPath 和 JSONata 状态中的字段的示意图。\]](http://docs.aws.amazon.com/zh_cn/step-functions/latest/dg/images/compare-jsonpath-jsonata.png)


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

 注意：**分配**和**输出**步骤**并行**进行。如果您选择在变量赋值期间转换数据，则转换后的数据将**不**可用于“输出”步骤。必须在 “输出” 步骤中重新应用 JSONata 转换。

![\[使用 JSONata 查询语言的状态的逻辑图。\]](http://docs.aws.amazon.com/zh_cn/step-functions/latest/dg/images/vars-jsonata.png)


## QueryLanguage 字段
<a name="querylanguage-field"></a>

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

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

## 用 JSON 字符串编写 JSONata 表达式
<a name="writing-jsonata-expressions-in-json-strings"></a>

 当 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 表达式。

## 保留变量：\$1states
<a name="transforming-reserved-variable-states"></a>

 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`总是指原始状态输入。

 对于 `Task`、`Parallel` 和 `Map` 状态：
+  **`$states.result`**如果成功，则指 API 或子工作流程的原始结果。
+  **`$states.errorOutput`**指的是 API 或子工作流程失败时的错误输出。

   `$states.errorOutput` 可用于 `Catch` 字段的 `Assign` 或 `Output`。

如果在无法访问 `$states.result` 或 `$states.errorOutput` 的字段和状态中尝试对其进行访问，则在创建、更新或验证状态机时将捕获该尝试。

`$states.context` 对象为您的工作流提供有关其具体执行的信息，例如 `StartTime`、任务令牌和初始工作流输入。要了解更多信息，请参阅[在 Step Functions 中从上下文对象访问执行数据](input-output-contextobject.md)。

## 处理表达式错误
<a name="handling-errors-jsonata-expressions"></a>

在运行时， JSONata 表达式求值可能由于多种原因而失败，例如：
+  **类型错误** - 如果 `$x` 或 `$y` 不是数字，则表达式（例如 `{% $x + $y %}`）将失败。
+  **类型不兼容** - 表达式的计算结果可能为该字段不接受的类型。例如，字段 `TimeoutSeconds` 需要数字输入，因此如果 `$timeout` 返回字符串，则表达式 `{% $timeout %}` 将失败。
+  **值超出范围** - 生成超出字段可接受范围的值的表达式将失败。例如，诸如 `{% $evaluatesToNegativeNumber %}` 之类的表达式在 `TimeoutSeconds` 字段中将会失败。
+  **未能返回结果** - JSON 不能表示未定义的值表达式，因此表达式 `{% $data.thisFieldDoesNotExist %}` 会导致错误。

在每种情况下，解释器都会引发错误：`States.QueryEvaluationError`。您的 Task、Map 和 Parallel 状态可以提供一个 `Catch` 字段用于捕获错误，并提供一个 `Retry` 字段用于在出错时重试。

## 从 JSONPath 转换为 JSONata
<a name="converting-from-jsonpath-to-jsonata"></a>

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

### 没有更多路径字段
<a name="no-more-path-fields"></a>

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

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

### JSON 对象
<a name="json-objects"></a>

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

### 没有更多 .\$1
<a name="no-more-"></a>

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

### Arguments 和 Output 字段
<a name="arguments-and-output-fields"></a>

 当设置`QueryLanguage`为时`JSONata`，旧的 I/O 处理字段将被禁用（`InputPath`、`Parameters``ResultSelector`、`ResultPath`和`OutputPath`），并且大多数州将获得两个新字段：`Arguments`和`Output`。

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

 `Arguments` 和 `Output` 字段（以及其他类似字段，如 `Map` 状态的 `ItemSelector`）将接受 JSON 对象，例如：

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

 或者，你可以直接使用 JSONata 表达式，例如：

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

 输出也可以接受任何类型的 JSON 值，例如：`"Output":true`、`"Output":42`。

 `Arguments`和`Output`字段仅支持 JSONata，因此在使用的工作流程中使用它们是无效的 JSONPath。相反，`InputPath`、`Parameters`、`ResultSelector`、`ResultPath``OutputPath`、和其他 JSONPath 字段仅在中受支持 JSONPath，因此在 JSONata 用作顶级工作流程或状态查询语言时，使用基于路径的字段是无效的。

### Pass 状态
<a name="pass-state"></a>

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

### Choice 状态
<a name="choice-state"></a>

 使用时 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 例子
<a name="jsonata-examples"></a>

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

### 示例：输入和输出
<a name="example-input-and-output"></a>

 此示例说明在您选择加入时`$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
  }
}
```

Test 状态或状态机执行将返回以下 JSON 输出：

```
{
  "lastName": "Last=>Rivera",
  "orderValue": 27.91
}
```

![\[屏幕截图显示了被测状态的输入和输出。\]](http://docs.aws.amazon.com/zh_cn/step-functions/latest/dg/images/jsonata-basic-io.png)


### 示例：使用筛选 JSONata
<a name="example-filtering-with-jsonata"></a>

 您可以使用 JSONata [路径运算符](https://docs.jsonata.org/path-operators)筛选数据。例如，假设您有一份可供输入的产品清单，而您只想处理卡路里含量为零的产品。您可以使用以下 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 表达式的输出示例。\]](http://docs.aws.amazon.com/zh_cn/step-functions/latest/dg/images/test-state-jsonata.png)


## JSONata Step Functions 提供的函数
<a name="jsonata-functions-provided-by-sfn"></a>

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

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

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

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

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

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

 这个函数需要三个参数。第一个参数是代表新数组第一个元素的整数，第二个参数是代表新数组最后一个元素的整数，第三个参数是新数组中元素的增量值整数。返回值是一个新生成的值数组，范围从函数的第一个参数到函数的第二个参数，两者之间的元素根据增量进行调整。增量值可以是正值，也可以是负值，它将从最后一个元素开始依次递增或递减，直至达到或超过最终值。

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

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

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

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

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

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

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

 之所以创建这个重载函数，是因为内置 JSONata 函数[https://docs.jsonata.org/numeric-functions#random](https://docs.jsonata.org/numeric-functions#random)不接受种子值。

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

 **\$1uuid**-`States.UUID` 内部函数的 JSONata 版本。

 此函数不接受任何参数。此函数会返回一个 v4 UUID。

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

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

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

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

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

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