Zum Inhalt

JSONata in Pantarey

JSONata ist die Abfrage- und Transformationssprache für JSON, mit der Pantarey Daten im Prozess formt, berechnete Felder rechnet und Prüfregeln auswertet. Ein Ausdruck liest aus einem JSON-Objekt und liefert ein neues Ergebnis: ein Feld, ein Objekt, eine Liste oder eine Zahl. Felder stehen ohne Klammern (antrag_db.mitarbeiter), Objekte werden wie JSON geschrieben, Funktionen beginnen mit $. Die vollständige Sprachbeschreibung mit allen Standardfunktionen steht auf jsonata.org; Pantarey verwendet die Bibliothek in Version 2.2.2 im Prozess, im Daten Designer und im Browser. Diese Seite beschreibt, was in Pantarey dazukommt: die Variablen, die Pantarey-Funktionen, die häufigsten Muster, das Verhalten im Fehlerfall und die Wege zum Testen.

Wo JSONata gilt

JSONata gilt überall, wo das ganze Feld ein Ausdruck ist: Input- und Output-Ausdruck jeder Aktivität, Input des Startereignisses, Input und Output des Datenbank-Elements, die JSONata-Felder der Service-Aufgaben (E-Mail-Empfänger, Anhänge, HTML-Dateninput, PDF-Daten, Graustufen-Eingabe), das Formular einer Benutzeraufgabe, berechnete Felder und Prüfregeln mit Sprache JSONata, der Ausdruck des Datensatz-Widgets und die Bedingungen der Planungstafel. Textfelder wie Betreff, Vorgangskennung oder Dateiname nehmen dagegen $(…)-Platzhalter. Die vollständige Zuordnung steht unter Welche Sprache wo?.

Variablen

Variable Bedeutung Wo
$ das aktuelle Objekt: im Prozess die Nachricht (Input) oder das Rohergebnis der Aktivität (Output); im Daten Designer der Datensatz oder, in einer Liste, die aktuelle Zeile überall
$$ die Wurzel der Eingabe, auch innerhalb von Listen und Funktionen überall
$root der ganze Datensatz, auch wenn $ gerade eine Listenzeile ist nur berechnete Felder und Prüfregeln
$old der gespeicherte Vorzustand des Datensatzes, null bei Neuanlage nur berechnete Felder
$$context Angaben zum letzten Schritt, von Pantarey in die Nachricht geschrieben Nachricht im Prozess

Im Prozess sind $root und $old nicht gesetzt. Ein berechnetes Feld in einer Liste greift mit $root.kunde auf den Datensatz zu, während menge * preis sich auf die Zeile bezieht, siehe Berechnete Felder.

$$context

Pantarey ergänzt die Nachricht nach jedem Schritt um das Objekt $$context. Weil der Name mit $$ beginnt, wird er in JSONata mit Backticks angesprochen: `$$context`.executedBy. In Platzhaltern genügt $($$context.executedBy). Welche Felder enthalten sind, hängt vom Schritt ab:

Quelle Felder in $$context
Benutzer-Start (Formular) activityType: "StartEvent", eventDefinition: "UserStartDefinitionType", executedBy (E-Mail), executedAt (ISO-Zeitstempel), selectedAction (wenn der Startknopf eine Aktion trägt)
Start per KI-Assistent (MCP) wie Benutzer-Start, zusätzlich startedVia: "mcp", mcpClient
Start per Datei-Upload wie Benutzer-Start, zusätzlich fileUpload: true; die Nachricht enthält uploadedFile bzw. uploadedFiles
Datensatz-Auslöser (Widget Laufende Timer) wie Benutzer-Start
Zeitplan (Timer-Start) activityType: "StartEvent", eventDefinition: "TimerEventDefinition", sonst nichts
Benutzeraufgabe abgeschlossen activityType: "bpmn:UserTask", finishedAt (Millisekunden), executedBy, executedAt, selectedAction (nur wenn eine Aktionsschaltfläche gedrückt wurde)
Benutzeraufgabe per KI-Assistent abgeschlossen zusätzlich completedVia: "mcp", mcpClient
Benutzeraufgabe nach Zeitlimit, Verhalten „fortfahren“ timedOut: true, completedBy: "timeout", activityType: "userTask" oder "externalUserTask"
Externe Aufgabe erledigt activityType: "externalUserTask", completedAt, completedBy: "external"; die Nachricht enthält außerdem selectedAction und formData
Externe Aufgabe asynchron activityType: "externalUserTask", executionMode: "async", completedAt, completedBy: "async-dispatch"
Prozessende activityType: "bpmn:Process", finishedAt
Dry Run executedBy: "dry-run@pantarey.io"

Die Vorgangs-ID und die Element-ID stehen nicht in $$context. Wird eine Nachricht mit $$context in einen Datensatz geschrieben, landet das Objekt als Feld im Datensatz; der Input des Datenbank-Elements entfernt es mit $sift($, function($v, $k) { $k != '$$context' }).

Pantarey-Funktionen

Zusätzlich zur JSONata-Standardbibliothek gibt es $fn('name', …) mit Funktionen für Text, Zahlen, Datum, Arbeitstage, Zähler und ULIDs, zum Beispiel $fn('date_toGermanDate', antrag_db.von) oder $fn('nextId', 'rechnung'). Der Katalog steht unter Template-Funktionen $fn. In JSONata erreichen die Argumente die Funktion als echte Werte (eine Zahl bleibt Zahl), in Platzhaltern als Text.

Zwei Einschränkungen: sql_list und sql_raw sind in JSONata nicht erlaubt („fn(): not allowed in jsonata“), sie gehören in SQL-Abfragen. Und bei der Live-Berechnung im Formular (berechnete Felder, Prüfregeln im Browser) werden Ausdrücke mit $fn( übersprungen; sie rechnen erst beim Speichern.

Die alten Schreibweisen $nextCounter('name'), $ulid(), $dateToPath(x) und $toGermanDate(x) sind weiter gültig und werden intern auf $fn umgeleitet.

Zehn Grundmuster

Alle Beispiele sind gegen die genannte Eingabe ausgewertet. Die Eingabe steht für die Nachricht im Prozess „Urlaub beantragen“ bzw. eine Materialbestellung der Musterfirma GmbH.

1. Feld lesen

Eingabe:

{ "antrag_db": { "mitarbeiter": "anna.funk@musterfirma.de", "von": "2026-07-13" } }

Ausdruck: antrag_db.mitarbeiter

Ergebnis: "anna.funk@musterfirma.de"

$.antrag_db.mitarbeiter ist dasselbe. Ein Pfad in ein fehlendes Feld liefert kein Ergebnis, siehe Fehlerverhalten.

2. Objekt bauen

Eingabe wie oben.

{
  "to": antrag_db.mitarbeiter,
  "subject": "Urlaub " & antrag_db.von
}

Ergebnis:

{ "to": "anna.funk@musterfirma.de", "subject": "Urlaub 2026-07-13" }

Schlüssel stehen in Anführungszeichen, Werte sind Ausdrücke. So entsteht die Eingabe für eine Service-Aufgabe.

3. Felder behalten oder entfernen

Eingabe:

{ "antrag_db": { "von": "2026-07-13", "bis": "2026-07-17", "status": "offen", "$$context": { "executedBy": "anna.funk@musterfirma.de" } } }

Entfernen mit $sift: $sift(antrag_db, function($v, $k) { $k != '$$context' })

Ergebnis: { "von": "2026-07-13", "bis": "2026-07-17", "status": "offen" }

Ergänzen oder überschreiben mit $merge: $merge([antrag_db, { "status": "genehmigt" }])

Ergebnis: { "von": "2026-07-13", "bis": "2026-07-17", "status": "genehmigt", "$$context": { … } }

Das zweite Objekt gewinnt bei gleichen Schlüsseln. Beides zusammen schreibt einen bereinigten Datensatz mit neuem Status.

4. Liste filtern

Eingabe:

{ "positionen": [
  { "artikel": "Monitor 27 Zoll", "menge": 2, "preis": 249 },
  { "artikel": "USB-C-Dock", "menge": 1, "preis": 129 }
] }

Ausdruck: positionen[menge > 1]

Ergebnis: { "artikel": "Monitor 27 Zoll", "menge": 2, "preis": 249 }

Ein Treffer kommt als einzelnes Objekt zurück, nicht als Liste. Soll das Ergebnis immer eine Liste sein, hängt man [] an: positionen[menge > 1][] ergibt [ { "artikel": "Monitor 27 Zoll", … } ].

5. Summe

Eingabe wie in Muster 4.

Ausdruck: $sum(positionen.(menge * preis))

Ergebnis: 627

positionen.(menge * preis) rechnet je Zeile, $sum addiert. Ebenso $max, $min, $average.

6. Bedingung

Eingabe:

{ "antrag_db": { "status": "genehmigt" }, "entscheidung": { "genehmigt": false } }

Ausdruck: antrag_db.status = "genehmigt" ? "Zusage" : "Absage"

Ergebnis: "Zusage"

Mit $exists gegen fehlende Felder absichern: $exists(entscheidung.begruendung) ? entscheidung.begruendung : "ohne Begründung" ergibt "ohne Begründung". Vergleich ist =, nicht ==; ungleich ist !=.

7. Zeichenketten

Eingabe:

{ "rechnung": { "nummer": 1024, "datum": "2026-11-11" } }

Ausdruck: "Rechnung " & rechnung.nummer & " vom " & $fn('date_toGermanDate', rechnung.datum)

Ergebnis: "Rechnung 1024 vom 11.11.2026"

& verbindet und wandelt Zahlen dabei in Text. $string(1024) wandelt ausdrücklich um, $uppercase, $substring, $replace, $split und $join sind weitere Standardfunktionen.

8. Datum

Eingabe:

{ "antrag_db": { "von": "2026-07-13" } }
{
  "heute": $substring($now(), 0, 10),
  "in_einer_woche": $fromMillis($toMillis(antrag_db.von) + 7 * 86400000, '[Y0001]-[M01]-[D01]'),
  "deutsch": $fn('date_toGermanDate', antrag_db.von)
}

Ergebnis am 11.11.2026:

{ "heute": "2026-11-11", "in_einer_woche": "2026-07-20", "deutsch": "13.07.2026" }

$now() liefert den ISO-Zeitstempel in UTC. Arbeitstage zählt $fn('date_workdays', von, bis) mit dem Arbeitskalender des Kontos.

9. Zählen

Eingabe wie in Muster 4.

Ausdruck: { "anzahl": $count(positionen), "gross": $count(positionen[menge > 1]), "leer": $count(nicht_da) }

Ergebnis: { "anzahl": 2, "gross": 1, "leer": 0 }

$count auf ein fehlendes Feld ergibt 0, kein Fehler. Deshalb eignet es sich für Bedingungen wie $count(positionen) > 0.

10. Dateien weiterreichen

Eingabe:

{ "rechnung": { "nummer": 1024 }, "rechnung_pdf": { "referenceId": "01HX7Z8K79G5T0ZJWBJ6C3V8PF", "filename": "document.pdf", "contentType": "application/pdf" } }
{
  "referenceId": rechnung_pdf.referenceId,
  "filename": "Rechnung_" & rechnung.nummer & ".pdf",
  "contentType": rechnung_pdf.contentType
}

Ergebnis:

{ "referenceId": "01HX7Z8K79G5T0ZJWBJ6C3V8PF", "filename": "Rechnung_1024.pdf", "contentType": "application/pdf" }

Eine Datei ist in der Nachricht ein Verweis mit referenceId, filename und contentType, Bilder zusätzlich mit thumbnail. JSONata behandelt ihn wie jedes Objekt; der Verweis kann unverändert weitergegeben (rechnung_pdf) oder wie hier mit neuem Namen gebaut werden. E-Mail-Anhänge verwerfen alles ohne referenceId.

Fehlerverhalten

Ein leeres Feld bedeutet: keine Transformation, die Daten gehen unverändert weiter. Liefert ein Ausdruck kein Ergebnis (in JSONata undefined, etwa bei einem Pfad in ein fehlendes Feld oder einem Filter ohne Treffer), meldet Pantarey:

Der Ausdruck liefert kein Ergebnis. Häufige Ursachen: das referenzierte Feld fehlt im aktuellen Datensatz, Tippfehler im Feldpfad (Groß-/Kleinschreibung beachten) oder ein Filter liefert keinen Treffer. Ausdruck: '…'. Verfügbare Felder im Datensatz: …

null ist dagegen ein gültiges Ergebnis. Was danach passiert, hängt vom Ort ab:

Ort Folge
Input-Ausdruck Der Schritt scheitert mit „Transformation (Input-Parameter) für Aktivität '…' fehlgeschlagen“. Pantarey speichert einen Wiederaufsetzpunkt mit der Nachricht vor der Transformation; im Process Explorer lässt sich die Nachricht anpassen und der Vorgang ab dieser Aktivität fortsetzen.
Output-Ausdruck Der Schritt scheitert mit RESULT_PARAMETER_ERROR, ebenfalls mit Wiederaufsetzpunkt und einem Vorschlag zum Fortfahren. Am Endereignis ist der Vorgang fehlgeschlagen.
Prüfregel (JSONata) Ein Auswertungsfehler, auch „kein Ergebnis“, lässt das Speichern scheitern („Validation rule … failed to evaluate“). Bei der Live-Prüfung im Formular wird ein Ausdruck ohne Ergebnis übersprungen. Deshalb Felder mit $exists absichern, siehe Prüfregeln.
Berechnetes Feld (JSONata) Das Speichern scheitert mit „Computed field … failed“. Ein Ergebnis null entfernt das Feld aus dem Datensatz.
E-Mail: Empfänger, Absender, Antwort an Schlägt JSONata fehl und der Text sieht wie eine Adresse aus, gilt der Text wörtlich; sonst scheitert der Schritt. Anhänge und HTML-Dateninput scheitern immer.
Gateway Trifft keine Bedingung zu und es gibt keinen Default-Pfad, scheitert der Schritt mit XOR_GATEWAY_ERROR und einer Liste aller Bedingungen und ihrer Ergebnisse.

Sicherheit: Zugriffe auf JavaScript-Interna ($.constructor, __proto__) liefern kein Objekt; die Bibliothek ist deshalb auf 2.2.2 festgelegt.

Grenzen

  • Input und Ergebnis des Startereignisses dürfen je 50 KB Text nicht überschreiten; die Validierung blockiert sonst den Deploy. Große Nachrichten im weiteren Prozess sind kein Problem, Pantarey lagert sie automatisch aus.
  • Ein Prozessschritt hat 360 Sekunden; eine Gateway-Bedingung 15 Sekunden. Ein JSONata-Ausdruck, der länger rechnet, lässt den Schritt scheitern. Rechenintensive Verarbeitung gehört in eine Service-Aufgabe.
  • Für Schleifen gilt: ab 200 KB Listendaten wechselt Pantarey in den verteilten Modus; die Grenzen der Durchläufe stehen unter Schleifen.

Testen

Live-Vorschau im JSONata-Editor. Im Dialog Daten & Variablen konfigurieren öffnet der Knopf mit dem Code-Symbol (Tooltip JSONata-Editor) neben dem Input- bzw. Output-Feld den Editor; in den Einstellungsdialogen der Service-Aufgaben (PDF-Daten-Mapping, E-Mail-Felder) heißt der Knopf Erweiterten Editor mit Live-Vorschau öffnen. Der Dialog Input-Parameter (JSONata) (bzw. Output) zeigt links Beispiel-Eingabedaten als editierbares JSON, rechts den JSONata-Ausdruck und darunter die Live-Vorschau, die nach jeder Änderung neu rechnet. Die Beispieldaten sind mit einem festen Beispiel (auftrag, kunde, positionen) vorbelegt und werden durch die eigene Nachricht ersetzt, etwa aus ⋮ → Nachrichtenschema oder aus Aktivitäten → In/Out eines Vorgangs; nur beim PDF-Daten-Mapping kommen sie aus den PDF-Beispieldaten. Ungültiges JSON meldet „Beispieldaten konnten nicht in gültiges JSON konvertiert werden.“ Übernehmen schreibt den Ausdruck ins Feld, Abbrechen verwirft.

Die Vorschau rechnet im Browser und ersetzt Backend-Funktionen durch Attrappen: $fn-Funktionen außer sql_* sind verfügbar, sys_ulid liefert immer denselben Wert, Zähler zählen nur im Browser hoch und verbrauchen keine Nummer, Arbeitstage kennen nur die bundesweiten Feiertage. {{value:…}} wird vor der Auswertung eingesetzt. Bei Dateiname und Vorgangskennung gibt es keinen Editor, weil dort Platzhalter gelten.

Dialog Input-Parameter (JSONata) für den Schritt Absage senden im Prozess Urlaub beantragen: links Beispiel-Eingabedaten mit antrag_db und entscheidung, rechts der JSONata-Ausdruck für die E-Mail und darunter die Live-Vorschau mit deutschem Datum und Empfängerliste, unten die Knöpfe Abbrechen und Übernehmen
Der JSONata-Editor mit Live-Vorschau: Beispieldaten links, Ausdruck und berechnetes Ergebnis rechts.

Dry Run. Der Dry Run im Prozess Designer spielt den Prozess mit einer Startnachricht aus dem Datenschema des Startformulars durch und prüft dabei Input, Output, Ergebnispfad, Vorgangskennung, Anzeigetext und Zuweisung jedes Elements. Zähler werden nur gelesen. Gateway-Bedingungen erkennt der Dry Run nur als wörtliches true oder false, alles andere nimmt den Default-Pfad.

KI-Assistent. Mit der Rolle Bauen steht dem KI-Assistenten das Werkzeug test_jsonata zur Verfügung: Ausdruck (bis 8000 Zeichen), Beispieldaten sowie optional root und old. Ein Ausdruck ohne Ergebnis kommt als Warnung zurück, ein Syntaxfehler mit Position und Token. Alle Beispiele dieser Seite sind so geprüft.

Beispiel

„Absage senden“ im Prozess „Urlaub beantragen“ der Musterfirma GmbH hat den Input-Ausdruck { "subject": "Urlaubsantrag abgelehnt", "contentTemplate": "<p>Der Urlaubsantrag vom {{von}} bis {{bis}} wurde abgelehnt.</p>", "contentData": { "von": $fn('date_toGermanDate', antrag_db.von), "bis": $fn('date_toGermanDate', antrag_db.bis) }, "toRecipients": [antrag_db.mitarbeiter] }. Im JSONata-Editor stehen links antrag_db und entscheidung aus dem Nachrichtenschema, rechts zeigt die Live-Vorschau "von": "13.07.2026" und die Empfängerliste. Fehlt antrag_db.bis in den Beispieldaten, meldet die Vorschau „Der Ausdruck liefert kein Ergebnis“ und nennt die vorhandenen Felder.

Häufige Fragen

Warum liefert mein Ausdruck „kein Ergebnis“?

Meist zeigt ein Pfad auf ein Feld, das in dieser Nachricht nicht existiert: Tippfehler, Groß-/Kleinschreibung, oder das Ergebnis eines früheren Schritts liegt unter einem anderen Ergebnispfad. Die Meldung nennt die vorhandenen Felder; ⋮ → Nachrichtenschema zeigt die Namen je Schritt. Optionales mit $exists(feld) ? feld : "" absichern.

Mein Feld heißt „Rechnungs-Nr: intern“. Wie spreche ich es an?

Namen mit Doppelpunkt, Leerzeichen, Bindestrich oder führendem $ stehen in Backticks: `Rechnungs-Nr: intern` oder `$$context`.executedBy.

Warum bekomme ich ein Objekt statt einer Liste mit einem Element?

JSONata gibt eine Liste mit genau einem Treffer als einzelnes Objekt zurück. positionen[menge > 1][] erzwingt eine Liste; $count(…) zählt in beiden Fällen richtig.

Zahl oder Text: warum scheitert betrag + 1?

Formularfelder vom Typ Text liefern "19.99" als Zeichenkette; + verlangt Zahlen („The left side of the + operator must evaluate to a number“). $number(betrag) + 1 rechnet, $string(1024) wandelt umgekehrt in Text, $type(feld) zeigt den Typ.

Nächster Schritt

Template-Funktionen $fn: der Katalog der Pantarey-Funktionen für Text, Zahlen, Datum, Zähler und SQL.