如何在 AWS Step Functions 中的数组中使用 jsonPath

How to use jsonPath inside array in AWS Step Functions

我正在编写一个 AWS 步骤函数,对于其中一个步骤,我希望调用一个接受数组作为输入之一的 lambda。但是,如果我尝试将 JsonPath 传递到数组中,我会得到

The value for the field 'arrayField.$' must be a STRING that contains a JSONPath but was an ARRAY

我的步进函数定义:

{
  "StartAt": "First",
  "States": {
  "First": {
    "Type": "Pass",
    "Parameters": {
      "type": "person"
    },
    "ResultPath": "$.output",
    "Next": "Second"
  },
    "Second": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:<aws_id>:function:MyFunction",
      "Parameters": {
        "regularParameter": "some string",
        "arrayParameter.$": ["$.output.type"]
      },
      "Next": "Succeed"
    },
    "Succeed": {
      "Type": "Succeed"
    }
  }
}

如何在数组中使用 jsonPath?

JSON参数字段中的路径必须是字符串。因此,如果你想向 lambda 函数传递一个名为 arrayParameter 的参数,你需要进行一个 jsonPath 查询来提取该数组。

例如,如果键输出中有一个名为 outputArray 的键,其值是数组。

输入JSON:

{
  "pre": "sdigf",
  "output": {
    "result": 1,
    "outputArray": ["test1","test2","test.."]
  }
}

参数语法:

"arrayParameter.$": "$.output.outputArray"

合理建议

我今天 运行 研究了数组中 JsonPath 解析的一个用例,发现(就像你一样)这个功能今天不存在。我最终决定在代码中进行数据处理更简单、更清晰。例如,您可以创建一个小的 Lambda,它接收 First 发出的对象并将其处理为 Second 可接受的格式并将其添加到输出(WaterKnight 在 ).

假设您出于某种原因无法更改 Second 中 Lambda 的输入格式(这将是此处的绝对最短路径)。

不合理的建议

就是说,如果您想要一种完全在 Step Functions 中完成此操作的方法(相当粗糙),您可以使用 Map state that executes Pass states 的结果。 Map 状态的输出是一个数组,聚合了每个组成 Pass 状态的输出。这些 Pass 状态只是使用 Parameters 属性在最终数组中发出您想要的值。下面是一个示例 Step Function 定义。我确实警告说这很恶心,我用不同的方式解决了这个问题。

 {
    "StartAt": "First",
    "Comment": "Please don't actually do this",
    "States": {
      "First": {
        "Type": "Pass",
        "Parameters": {
          "type": "person"
        },
        "ResultPath": "$.output",
        "Next": "Add Array"
      },
      "Add Array": {
        "Comment": "A Map state needs some array to loop over in order to work. We will give it a dummy array. Add an entry for each value you intend to have in the final array. The values here don't matter.",
        "Type": "Pass",
        "Result": [
          0
        ],
        "ResultPath": "$.dummy",
        "Next": "Mapper"
      },
      "Mapper": {
        "Comment": "Add a Pass state with the appropriate Parameters for each field you want to map into the output array",
        "Type": "Map",
        "InputPath": "$",
        "ItemsPath": "$.dummy",
        "Parameters": {
          "output.$": "$.output"
        },
        "Iterator": {
          "StartAt": "Massage",
          "States": {
            "Massage": {
              "Type": "Pass",
              "Parameters": {
                "type.$": "$.output.type"
              },
              "OutputPath": "$.type",
              "End": true
            }
          }
        },
        "ResultPath": "$.output.typeArray",
        "Next": "Second"
      },
      "Second": {
        "Comment": "The Lambda in your example is replaced with Pass so that I could test this",
        "Type": "Pass",
        "Parameters": {
          "regularParameter": "some string",
          "arrayParameter.$": "$.output.typeArray"
        },
        "Next": "Succeed"
      },
      "Succeed": {
        "Type": "Succeed"
      }
    }
  }

正如@Seth Miller 所提到的,不幸的是,数组中的 JsonPath 解析不起作用。如果数组中要替换的值的数量很小并且已知有一个简单的解决方法(在我的例子中我需要一个大小为 1 的数组)。

步骤是:

  1. 用您需要的值的数量初始化数组;
  2. 使用 "ResultPath": "$.path.to.array[n]";
  3. 替换每个值
  4. 在你的任务中使用"$.path.to.array"

简单有效的示例:

{
  "StartAt": "First",
  "States": {
    "First": {
      "Type": "Pass",
      "Parameters": {
        "type": "person"
      },
      "ResultPath": "$.output",
      "Next": "Initialise Array"
    },
    "Initialise Array": {
      "Comment": "Add an entry for each value you intend to have in the final array, the values here don't matter.",
      "Type": "Pass",
      "Parameters": [
        0
      ],
      "ResultPath": "$.arrayParameter",
      "Next": "Fill Array"
    },
    "Fill Array": {
      "Comment": "Replace the first entry of array with parameter",
      "Type": "Pass",
      "InputPath": "$.output.type",
      "ResultPath": "$.arrayParameter[0]",
      "End": true
    }
  }
}

并在您的任务示例中使用生成的数组:

    "Second": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:<aws_id>:function:MyFunction",
      "Parameters": {
        "regularParameter": "some string",
        "arrayParameter.$": "$.arrayParameter"
      },
      "Next": "Succeed"
    },

正如许多答案正确指出的那样,不可能完全按照您需要的方式进行。但我会建议另一种解决方案:一系列字典。它不完全是您所需要的,但它是原生的而不是 hacky。

"Second": {
  "Type": "Task",
  "Resource": "arn:aws:lambda:us-east-1:<aws_id>:function:MyFunction",
  "Parameters": {
    "regularParameter": "some string",
    "arrayParameter": [{"type.$": "$.output.type"}]
  },
  "Next": "Succeed"
},

结果会是

{
  "regularParameter": "some string",
  "arrayParameter": [{"type": "SingleItemWrappedToAnArray"}]
}

解决此问题的另一种方法是使用输出对象数组的并行状态,然后使用 jsonPath 将其转换为简单数组:

{
  "StartAt": "Parallel",
  "States": {
    "Parallel": {
      "Type": "Parallel",
      "Next": "Use Array",
      "ResultPath": "$.items",
      "Branches": [
        {
          "StartAt": "CreateArray",
          "States": {
            "CreateArray": {
              "Type": "Pass",
              "Parameters": {
                "value": "your value"
              },
              "End": true
            }
          }
        }
      ]
    },
    "Use Array": {
      "Type": "Pass",
      "Parameters": {
        "items.$": "$.items[*].value"
      },
      "End": true
    }
  }
}

在此示例中,并行状态输出以下内容json:

{
  "items": [
    {
      "value": "your value"
    }
  ]
}

并且"Use Array"状态产生:

{
  "items": [
    "your value"
  ]
}

从新版本开始,您可以使用内部函数 States.Array:

  "arrayParameter.$": "States.Array($.output.type)"

https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html