View a markdown version of this page

Transformer les données avec JSonata dans Step Functions - AWS Step Functions

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Transformer les données avec JSonata dans Step Functions

Avec JSonata, vous bénéficiez d'un puissant langage de requête et d'expression open source pour sélectionner et transformer les données dans vos flux de travail. Pour une brève introduction et une référence complète à JSonata, consultez JSONata.org la documentation.

Version de JSonata prise en charge

Step Functions prend en charge la version 2.0.6 de JSonata.

La vidéo suivante décrit les variables et JSonata dans Step Functions à l'aide d'un exemple DynamoDB :

Vous devez choisir d'utiliser le langage de requête et de transformation JSonata pour les flux de travail existants. Lorsque vous créez un flux de travail dans la console, nous vous recommandons de choisir JSonata pour la machine à états de niveau supérieur. QueryLanguage Pour les flux de travail existants ou nouveaux qui utilisent JSONPath, la console fournit une option permettant de convertir les états individuels en JSonata.

Après avoir sélectionné JSonata, vos champs de flux de travail seront réduits de cinq champs JSONPath (InputPath,, Parameters ResultSelectorResultPath, etOutputPath) à deux champs seulement : et. Arguments Output De plus, vous n'utiliserez pas .$ les noms de clés des objets JSON.

Si vous débutez avec Step Functions, il vous suffit de savoir que les expressions JSonata utilisent la syntaxe suivante :

Syntaxe JSonata : "{% <JSONata expression> %}"

Les exemples de code suivants montrent une conversion de JSONPath en 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" } }

Compte tenu de l'entrée { "title" : "Doctor" } et de la variable customerName assignées"María", les deux machines d'état produiront le résultat JSON suivant :

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

Dans le schéma suivant, vous pouvez voir une représentation graphique montrant comment la conversion de JSONPath (à gauche) en JSonata (à droite) réduira la complexité des étapes dans vos machines d'état :

Schéma qui compare les champs dans les états JSONPath et JSonata.

Vous pouvez (éventuellement) sélectionner et transformer les données de l'entrée d'état en arguments à envoyer à votre action intégrée. Avec JSonata, vous pouvez ensuite (éventuellement) sélectionner et transformer les résultats de l'action pour l'assignation aux variables et pour l'état Output.

Remarque : Les étapes d'assignation et de sortie se déroulent en parallèle. Si vous choisissez de transformer les données lors de l'attribution de variables, ces données transformées ne seront pas disponibles à l'étape Sortie. Vous devez réappliquer la transformation JSonata à l'étape Output.

Schéma logique d'un état utilisant le langage de requête JSonata.

QueryLanguage champ

Dans les définitions ASL de votre flux de travail, il existe un QueryLanguage champ au niveau supérieur d'une définition de machine à états et dans des états individuels. En définissant QueryLanguage des états individuels, vous pouvez adopter progressivement JSonata dans une machine d'état existante plutôt que de mettre à niveau la machine d'état en une seule fois.

Le QueryLanguage champ peut être défini sur "JSONPath" ou"JSONata". Si le QueryLanguage champ de niveau supérieur est omis, sa valeur par défaut est. "JSONPath" Si un état contient un QueryLanguage champ au niveau de l'état, Step Functions utilisera la langue de requête spécifiée pour cet état. Si l'état ne contient aucun QueryLanguage champ, il utilisera la langue de requête spécifiée dans le QueryLanguage champ de niveau supérieur.

Écrire des expressions JSonata dans des chaînes JSON

Lorsqu'une chaîne contenant la valeur d'un champ ASL, d'un champ d'objet JSON ou d'un élément de tableau JSON est entourée de {% %} caractères, cette chaîne est évaluée comme JSonata. Notez que la chaîne doit {% commencer sans espaces de début et doit se terminer %} sans espaces de fin. L'ouverture ou la fermeture incorrectes de l'expression entraînera une erreur de validation.

Voici quelques exemples :

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

  • "Arguments" : {"field1" : "{% $name %}"}dans un Task État

  • "Items": [1, "{% $two %}", 3]dans un Map État

Tous les champs ASL n'acceptent pas JSonata. Par exemple, Type le champ de chaque État doit être défini sur une chaîne constante. De même, le Resource champ de Task l'état doit être une chaîne constante. Le Items champ Map d'état acceptera un tableau JSON, un objet JSON ou une expression JSonata qui doit être évaluée en tableau ou en objet.

Variable réservée : $states

Step Functions définit une variable réservée unique appelée $states. Dans les états JSonata, les structures suivantes sont attribuées $states pour être utilisées dans les expressions 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 }

Lors de la saisie de l'état, Step Functions affecte l'état saisi à $states.input. La valeur de $states.input peut être utilisée dans tous les champs qui acceptent les expressions JSonata. $states.inputfait toujours référence à l'entrée d'état d'origine.

Pour TaskParallel, et Map déclare :

  • $states.resultfait référence au résultat brut de l'API ou du sous-flux de travail en cas de réussite.

  • $states.errorOutputfait référence au résultat d'erreur en cas d'échec de l'API ou du sous-flux de travail.

    $states.errorOutputpeut être utilisé Catch sur le terrain Assign ouOutput.

Toute tentative d'accès $states.result ou $states.errorOutput dans des champs et états où ils ne sont pas accessibles sera interceptée lors de la création, de la mise à jour ou de la validation de la machine à états.

L'$states.contextobjet fournit à vos flux de travail des informations sur leur exécution spécifiqueStartTime, telles que le jeton de tâche et la saisie initiale du flux de travail. Pour en savoir plus, consultezAccès aux données d'exécution depuis l'objet Context dans Step Functions.

Gestion des erreurs d'expression

Au moment de l'exécution, l'évaluation des expressions JSonata peut échouer pour diverses raisons, telles que :

  • Erreur de type : une expression, telle que{% $x + $y %}, échouera si elle $x n'$yest pas un nombre.

  • Incompatibilité de type : une expression peut être d'un type que le champ n'acceptera pas. Par exemple, le champ TimeoutSeconds nécessite une entrée numérique, de sorte que l'expression {% $timeout %} échouera si elle $timeout renvoie une chaîne.

  • Valeur hors plage : une expression qui produit une valeur située en dehors de la plage acceptable pour un champ échouera. Par exemple, une expression telle que {% $evaluatesToNegativeNumber %} échouera dans le TimeoutSeconds champ.

  • Impossible de renvoyer un résultat : le JSON ne peut pas représenter une expression de valeur non définie. L'expression {% $data.thisFieldDoesNotExist %} provoquerait donc une erreur.

  • Limite de mémoire dépassée : une expression JSonata qui consomme trop de mémoire pendant l'évaluation échouera avec une Expression evaluation memory limit exceeded erreur. Cela peut se produire avec des expressions qui traitent ou transforment de grandes quantités de données. Pour contourner cette limitation, envisagez de déplacer la transformation des données vers une fonction Lambda.

  • Expression timeout : une expression JSonata dont l'évaluation prend plus d'une seconde échouera avec une erreur. Expression evaluation timeout Cela peut se produire avec des expressions contenant des boucles infinies ou des opérations très coûteuses.

  • Stack Overflow : une expression JSonata qui dépasse la profondeur de récursion maximale échouera avec un. Stack overflow error Si la récursivité n'est pas terminale, assurez-vous que le scénario de base ou la condition de terminaison de la fonction sont corrects. Si la récursivité prend fin mais que la pile d'appels devient trop profonde, envisagez de réécrire la fonction en tant que récursive pour réduire la profondeur de la pile.

Dans chaque cas, l'interpréteur lancera l'erreur :States.QueryEvaluationError. Vos états Task, Map et Parallel peuvent fournir un Catch champ pour détecter l'erreur et un Retry champ pour réessayer de corriger l'erreur.

Conversion de JSONPath en JSonata

Les sections suivantes comparent et expliquent les différences entre le code écrit avec JSONPath et JSonata.

Plus de champs de chemin

L'ASL exige que les développeurs utilisent des Path versions de champs, comme dansTimeoutSecondsPath, pour sélectionner une valeur à partir des données d'état lors de l'utilisation de JSONPath. Lorsque vous utilisez JSonata, vous n'utilisez plus de Path champs car ASL interprétera automatiquement pour vous les expressions JSonata {% %} incluses dans des champs autres que Path, tels que. TimeoutSeconds

  • Exemple d'ancienne version de JSONPath : "TimeoutSecondsPath": "$timeout"

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

De même, l'Mapétat ItemsPath a été remplacé par le Items champ qui accepte un tableau JSON, un objet JSON ou une expression JSonata qui doit être évaluée en tableau ou en objet.

Objets JSON

ASL utilise le terme modèle de charge utile pour décrire un objet JSON qui peut contenir des expressions JSONPath et des valeurs de Parameters champ. ResultSelector ASL n'utilisera pas le terme modèle de charge utile pour JSonata car l'évaluation de JSonata a lieu pour toutes les chaînes, qu'elles apparaissent seules ou à l'intérieur d'un objet JSON ou d'un tableau JSON.

Plus rien. $

L'ASL vous oblige à ajouter « .$ » aux noms de champs dans les modèles de charge utile pour utiliser JSONPath et les fonctions intrinsèques. Lorsque vous spécifiez"QueryLanguage":"JSONata", vous n'utilisez plus la convention « .$ » pour les noms de champs d'objets JSON. Au lieu de cela, vous insérez les expressions JSonata dans des caractères. {% %} Vous utilisez la même convention pour tous les champs à valeur de chaîne, quelle que soit la profondeur d'imbrication de l'objet dans d'autres tableaux ou objets.

Arguments et champs de sortie

Lorsque le QueryLanguage paramètre est défini surJSONata, les anciens champs de I/O traitement seront désactivés (InputPathParameters,ResultSelector, ResultPath etOutputPath) et la plupart des États recevront deux nouveaux champs : Arguments etOutput.

JSonata fournit un moyen plus simple d'effectuer des I/O transformations par rapport aux champs utilisés avec JSONPath. Les fonctionnalités de JSonata le rendent Arguments Output plus performant que les cinq champs précédents avec JSONPath. Ces nouveaux noms de champs contribuent également à simplifier votre ASL et à clarifier le modèle de transmission et de renvoi de valeurs.

Les Output champs Arguments and (et d'autres champs similaires tels que ceux de Map l'étatItemSelector) accepteront soit un objet JSON tel que :

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

Vous pouvez également utiliser directement une expression JSonata, par exemple :

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

La sortie peut également accepter n'importe quel type de valeur JSON, par exemple :"Output":true,"Output":42.

Les Output champs Arguments et ne prennent en charge que JSonata, il n'est donc pas possible de les utiliser avec des flux de travail utilisant JSONPath. À l'inverse,InputPath,Parameters,, ResultSelector ResultPathOutputPath, et les autres champs JSONPath ne sont pris en charge que dans JSONPath. Il n'est donc pas possible d'utiliser des champs basés sur des chemins lorsque vous utilisez JSonata comme flux de travail de haut niveau ou langage de requête d'état.

État de passage

L'état facultatif Result in a Pass était auparavant traité comme le résultat d'une tâche virtuelle. Lorsque JSonata est sélectionné comme langage de flux de travail ou de requête d'état, vous pouvez désormais utiliser le nouveau champ de sortie.

État du choix

Lorsque vous utilisez JSONPath, les états de choix ont une entrée Variable et de nombreux chemins de comparaison, tels que les suivants : 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" } ], }

Avec JSonata, l'état de choix indique Condition où vous pouvez utiliser une expression JSonata :

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

Remarque : Les variables et les champs de comparaison ne sont disponibles que pour JSONPath. La condition n'est disponible que pour JSonata.

Exemples de JSonata

Les exemples suivants peuvent être créés dans Workflow Studio pour expérimenter avec JSonata. Vous pouvez créer et exécuter les machines à états, ou utiliser l'état Test pour transmettre des données et même modifier la définition des machines à états.

Exemple : entrée et sortie

Cet exemple montre comment utiliser l'$states.inputentrée d'état et le Output champ pour spécifier l'état de sortie lorsque vous optez pour 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 %}" } } } }

Lorsque le flux de travail est exécuté avec les informations suivantes en entrée :

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

L'état du test ou l'exécution de la machine à états renverra la sortie JSON suivante :

{ "lastName": "Last=>Rivera", "orderValue": 27.91 }
Capture d'écran montrant l'entrée et la sortie d'un état en cours de test.

Exemple : Filtrage avec JSonata

Vous pouvez filtrer vos données à l'aide des opérateurs JSonata Path. Imaginons, par exemple, que vous ayez une liste de produits à saisir et que vous ne vouliez traiter que des produits ne contenant aucune calorie. Vous pouvez créer une définition de machine à états avec l'ASL suivante et tester l'FilterDietProductsétat avec l'exemple d'entrée qui suit.

Définition de machine à états pour le filtrage avec JSonata

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

Exemple d'entrée pour le test

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

Résultat du test de l'étape dans votre machine à états

{ "dietProducts": [ { "calories": 0, "flavour": "Cola", "name": "Product-2" }, { "calories": 0, "flavour": "Lime", "name": "Product-5" } ] }
Exemple de sortie pour les expressions JSonata en cours de test.

Exemple : utilisation de la sortie d'état précédente dans un état de carte

Cet exemple montre comment utiliser la sortie d'un état précédent comme entrée pour un état de carte avec JSonata. Le flux de travail suivant utilise un état Pass pour simuler une tâche qui renvoie un ordre avec une liste d'éléments, puis un état Map sélectionne le tableau d'éléments dans cette sortie et itère dessus.

Définition de la machine à états

{ "Comment": "Example: Using previous state output in a Map state with JSONata", "QueryLanguage": "JSONata", "StartAt": "GetOrder", "States": { "GetOrder": { "Type": "Pass", "Output": { "orderId": "{% $states.input.orderId %}", "items": "{% $states.input.items %}" }, "Next": "ProcessItems" }, "ProcessItems": { "Type": "Map", "Items": "{% $states.input.items %}", "ItemProcessor": { "ProcessorConfig": { "Mode": "INLINE" }, "StartAt": "CalculateItemTotal", "States": { "CalculateItemTotal": { "Type": "Pass", "Output": { "name": "{% $states.input.name %}", "total": "{% $states.input.price * $states.input.quantity %}" }, "End": true } } }, "End": true } } }

Dans cette définition, l'GetOrderétat affiche les données de commande inchangées. L'état de ProcessItems la carte est utilisé "Items": "{% $states.input.items %}" pour sélectionner le items tableau à partir de la sortie deGetOrder. Chaque itération reçoit un article du tableau et calcule le total de l'article en multipliant le prix par la quantité.

Exemple d'entrée

{ "orderId": "ORD-1234", "items": [ { "name": "Widget", "price": 4.99, "quantity": 3 }, { "name": "Gadget", "price": 12.50, "quantity": 2 }, { "name": "Bolt", "price": 0.75, "quantity": 10 } ] }

Résultat attendu

[ { "name": "Widget", "total": 14.97 }, { "name": "Gadget", "total": 25 }, { "name": "Bolt", "total": 7.5 } ]

Exemple : aplatissement de la sortie à état parallèle

Lorsque vous utilisez un état parallèle, il renvoie un tableau où chaque élément est la sortie d'une branche. Avec JSonata, vous pouvez utiliser le Output champ à l'état parallèle pour aplatir ou fusionner ces résultats en un seul objet. Cette approche remplace le champ JSONPathResultSelector.

L'exemple suivant utilise un état parallèle avec deux branches. Chaque branche simule un appel GetItem DynamoDB en utilisant un état Pass. L'état Parallèle est utilisé $merge($states.result) dans son Output champ pour fusionner les résultats de la branche en un seul objet.

Définition de la machine à états

{ "Comment": "Example: Flattening Parallel state output with JSONata", "QueryLanguage": "JSONata", "StartAt": "GetOrderAndCustomer", "States": { "GetOrderAndCustomer": { "Type": "Parallel", "Output": "{% $merge($states.result) %}", "Branches": [ { "StartAt": "Get Order", "States": { "Get Order": { "Type": "Pass", "Output": { "orderId": "{% $states.input.orderId %}", "orderDate": "2024-11-20", "total": 35.99 }, "End": true } } }, { "StartAt": "Get Customer", "States": { "Get Customer": { "Type": "Pass", "Output": { "customerId": "{% $states.input.customerId %}", "customerName": "Martha Rivera", "email": "martha@example.com" }, "End": true } } } ], "Next": "Done" }, "Done": { "Type": "Succeed" } } }

Exemple d'entrée

{ "orderId": "12345", "customerId": "C-100" }

Résultat attendu

{ "orderId": "12345", "orderDate": "2024-11-20", "total": 35.99, "customerId": "C-100", "customerName": "Martha Rivera", "email": "martha@example.com" }

La $merge() fonction combine un ensemble d'objets en un seul objet. Si les branches renvoient des objets dont les clés se chevauchent, les éléments du tableau ultérieurs ont la priorité. Les résultats des branches sont ordonnés de manière cohérente : ils correspondent à l'ordre du Branches tableau dans la définition de la machine à états.

Fonctions JSonata fournies par Step Functions

JSonata contient des bibliothèques de fonctions pour les fonctions de chaîne, de numérique, d'agrégation, de booléen, de tableau, d'objet et d'ordre élevé Date/Time. Step Functions fournit des fonctions JSonata supplémentaires que vous pouvez utiliser dans vos expressions JSonata. Ces fonctions intégrées remplacent les fonctions intrinsèques de Step Functions. Les fonctions intrinsèques ne sont disponibles que dans les états qui utilisent le langage de requête JSONPath.

Remarque : les fonctions Built-in JSonata qui nécessitent des valeurs entières comme paramètres arrondiront automatiquement les nombres non entiers fournis.

$partition - Équivalent JSonata d'une fonction States.ArrayPartition intrinsèque pour partitionner un grand tableau.

Le premier paramètre est le tableau à partitionner, le second est un entier représentant la taille du bloc. La valeur de retour sera un tableau bidimensionnel. L'interpréteur découpe le tableau d'entrée en plusieurs tableaux de la taille spécifiée par la taille du morceau. La longueur du dernier segment de tableau peut être inférieure à la longueur des fragments de tableau précédents si le nombre d'éléments restants dans le tableau est inférieur à la taille du morceau.

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

$range - Équivalent JSonata d'une fonction States.ArrayRange intrinsèque pour générer un tableau de valeurs.

Cette fonction prend trois arguments. Le premier argument est un entier représentant le premier élément du nouveau tableau, le deuxième argument est un entier représentant le dernier élément du nouveau tableau, et le troisième argument est l'entier de valeur delta pour les éléments du nouveau tableau. La valeur de retour est un tableau nouvellement généré de valeurs allant du premier argument de la fonction au deuxième argument de la fonction, les éléments intermédiaires étant ajustés par le delta. La valeur delta peut être positive ou négative, ce qui augmentera ou diminuera chaque élément depuis le dernier jusqu'à ce que la valeur finale soit atteinte ou dépassée.

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

$hash - Équivalent JSonata de la fonction States.Hash intrinsèque pour calculer la valeur de hachage d'une entrée donnée.

Cette fonction prend deux arguments. Le premier argument est la chaîne source à hacher. Le deuxième argument est une chaîne représentant l'algorithme de hachage utilisé pour le calcul du hachage. L'algorithme de hachage doit prendre l'une des valeurs suivantes :"MD5",,"SHA-1", "SHA-256""SHA-384","SHA-512". La valeur renvoyée est une chaîne du hachage calculé des données.

Cette fonction a été créée car JSonata ne prend pas en charge nativement la possibilité de calculer des hachages.

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

$random - Équivalent JSonata de la fonction States.MathRandom intrinsèque pour renvoyer un nombre aléatoire n où. 0 ≤ n < 1

La fonction prend un argument entier facultatif représentant la valeur initiale de la fonction aléatoire. Si vous utilisez cette fonction avec la même valeur initiale, elle renvoie un nombre identique.

Cette fonction surchargée a été créée parce que la fonction JSonata intégrée n'accepte $randompas de valeur initiale.

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

$uuid - Version JSonata de la fonction intrinsèque. States.UUID

La fonction ne prend aucun argument. Cette fonction renvoie un UUID v4.

Cette fonction a été créée car JSonata ne prend pas en charge nativement la possibilité de générer des UUID.

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

$parse - Fonction JSonata pour désérialiser les chaînes JSON.

La fonction prend un JSON sous forme de chaîne comme seul argument.

JSonata prend en charge cette fonctionnalité via $eval ; toutefois, elle n'$evalest pas prise en charge dans les flux de travail Step Functions.

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