Skip to Content
Mélodium 0.10.4 is now available!
DocsExemples08. Transformation JavaScript

Transformation JavaScript

Source: tutorial/08_javascript_transform See in Playground

Lit un objet JSON par ligne, calcule une note en JavaScript à partir de son champ score, et réécrit les enregistrements notés. Les données d’exemple, students.jsonl, utilisent des noms avec des caractères UTF-8 complets (Amélie, Øystein, Carmiña, Łukasz), lus et réécrits sans aucun traitement particulier.

Exécution

cd tutorial/08_javascript_transform melodium run Compo.toml --input_file students.jsonl
{"grade":"A","name":"Amélie","score":92} {"grade":"E","name":"Øystein","score":58} {"grade":"C","name":"Carmiña","score":74} {"grade":"FX","name":"Łukasz","score":45}

Optionnel : ajoutez --api-report et un jeton d’API (MELODIUM_API_TOKEN) pour voir la trace complète de cette exécution sur Cadence.CI .

Fonctionnement

Le modèle Grader charge une fonction JavaScript grade() une seule fois au démarrage, et cette même fonction compilée est réutilisée pour chaque ligne traitée :

model Grader() : JavaScriptEngine { code = ${{function grade(input) { var g = 'F'; if (input.score >= 90) g = 'A'; else if (input.score >= 80) g = 'B'; else if (input.score >= 70) g = 'C'; else if (input.score >= 60) g = 'D'; else if (input.score >= 50) g = 'E'; else if (input.score >= 45) g = 'FX'; return { name: input.name, score: input.score, grade: g }; } }} }

La chaîne en bloc brut ${{...}} est la façon standard d’embarquer du code JavaScript multiligne sans avoir à échapper les sauts de ligne ou les guillemets.

Les lignes sont extraites de la même façon que dans les exemples précédents de traitement de texte (split, flatten, trim, lignes vides écartées), puis chaque ligne est analysée en une valeur Json et transmise à process, qui appelle la fonction JS grade(value) définie dans le code du modèle :

treatment main( const input_file: string = "students.jsonl", const output: string = "grades.txt" ) model engine: Grader() { startup() read: readTextLocal(path=input_file) startup.trigger -> read.trigger splitLines: split(delimiter="\n", inclusive=false) lines: flatten<string>() trimmed: trim() read.text -> splitLines.text,splitted -> lines.vector,value -> trimmed.text isBlank: exact(pattern="") notBlank: not<bool>() nonBlank: filter<string>() trimmed.trimmed -> isBlank.text isBlank.matches -> notBlank.value trimmed.trimmed -> nonBlank.value notBlank.not -> nonBlank.select parsed: toJson() body: unwrapOr<Json>(default=|null()) graded: process[engine=engine](code="grade(value)") result: unwrapOr<Json>(default=|null()) asText: toString<Json>() nonBlank.accepted -> parsed.text,json -> body.option,value -> graded.value,result -> result.option,value -> asText.value,into -> logGrades.messages logGrades: logInfos(label="grade") withNewline: entry(key="line") asLine: format(format="{line}\n") write: writeTextLocal(path=output) logDone: logInfoMessage(label="js", message="grades written") asText.into -> withNewline.value,map -> asLine.entries,formatted -> write.text write.finished -> logDone.trigger }

À l’intérieur de la fonction JavaScript, value est l’objet analysé (input.score, input.name), exactement l’accès champ par champ que les traitements json seuls ne permettent pas, comme vu dans les deux étapes précédentes du tutoriel. process renvoie un Option<Json> (none si le code lève une exception ou renvoie quelque chose qui ne peut pas être converti en JSON), et unwrapOr fournit alors une valeur de repli pour que le pipeline ne se bloque jamais sur un enregistrement invalide.

Deux points de dépaquetage distincts méritent d’être notés :

  • unwrapOr<Json>(default=|null()) avant process : un JSON d’entrée malformé devient silencieusement null plutôt que d’interrompre le pipeline.
  • unwrapOr<Json>(default=|null()) après process : une erreur d’exécution JS produit elle aussi un null gracieux plutôt que d’arrêter l’exécution.

Chaque résultat est journalisé et écrit dans grades.txt, un objet JSON par ligne, en reprenant le même idiome “entry + format + saut de ligne” utilisé pour construire des rapports texte tout au long du tutoriel. Comme le code du modèle est compilé une seule fois au démarrage, et que le paramètre code de process ("grade(value)") n’est que l’expression évaluée pour chaque élément, la logique de transformation n’est écrite qu’une fois et réutilisée pour chaque ligne sans recompilation. Cela fait de javascript::process la solution de repli pour l’accès structuré au JSON : dès qu’une tâche nécessite de lire ou de construire des champs précis d’un objet ou d’un tableau JSON, c’est l’outil à privilégier plutôt que d’essayer de l’assembler uniquement à partir des primitives du paquet json.

Dépendances

[dependencies] std = "0.10.4" # flux de base, journalisation, structures de données fs = "0.10.4" # lecture/écriture de fichiers locaux json = "0.10.4" # parsing et sérialisation JSON javascript = "0.10.4" # moteur JavaScript embarqué